diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 8c48a20e744d12..9ee93ce98120df 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "microsoft.dotnet.xharness.cli": { - "version": "1.0.0-prerelease.21201.1", + "version": "1.0.0-prerelease.21209.2", "commands": [ "xharness" ] diff --git a/Directory.Build.props b/Directory.Build.props index be3d42e18ba93f..4ee7e1db360f89 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -233,7 +233,7 @@ MIT $(CopyrightNetFoundation) $(MSBuildThisFileDirectory)THIRD-PARTY-NOTICES.TXT - https://go.microsoft.com/fwlink/?LinkID=799421 + https://go.microsoft.com/fwlink/?LinkID=799421 true diff --git a/docs/design/mono/mobile-runtimeconfig-json.md b/docs/design/mono/mobile-runtimeconfig-json.md new file mode 100644 index 00000000000000..239826da05220e --- /dev/null +++ b/docs/design/mono/mobile-runtimeconfig-json.md @@ -0,0 +1,142 @@ +# Mobile runtimeconfig.json host configuration + +## Motivation + +`runtimeconfig.json` is a file used by .NET 5+ applications to pass arbitrary configuration parameters via the application host to the .NET runtime. On mobile and WebAssembly, we would like to support passing configuration properties from the Mono embedder to the runtime. + +To minimize the impact on the app startup time, the design constraints are as follows: +1. Don’t parse JSON at runtime during application startup +2. Don’t need to support shared framework configuration parameters +3. Don't need to support the desktop host environment variables, startup hooks, or other configuration mechanisms that do not make sense on mobile. +4. This is separate from the key/value set passed to `monovm_initialize`/`coreclr_initialize` (ie we will not pass the additional properties via `monovm_initialize` - just using this new mechanism). Things like the TPA list, additional probing paths, PINVOKE_OVERRIDE, etc will be addressed by an evolution of `monovm_initialize` - https://github.com/dotnet/runtime/issues/48416 + +## Design Overview + +We break up runtimeconfig.json loading into two parts: +1. A new MSBuild task called `RuntimeConfigParser` will run after the `runtimeconfig.json` is created by the `dotnet build` process. The task will convert the properties and their values into a binary blob format. The resulting `runtimeconfig.bin` file will be bundled with the application. +2. The runtime will expose a new API entrypoint `monovm_runtimeconfig_initialize` that gets either a path to pass to `mono_file_map_open` or a pointer to the blob in memory. When called, the runtime will read the binary data and populate the managed AppContext with the properties. + +We will only use the `runtimeOptions→configProperties` json key. Its content is a JSON dictionary with string keys and string/bool/numeric values. We convert the values to strings when we store them in the binary runtimeconfig.bin, which is the same way they are treated by the default host. + +The runtime assumes that the properties passed via `monovm_initialize` and `monovm_runtimeconfig_initialize` will be different. To ensure this, the provided MSBuild task will be passed a list of property names that the embedder promises it will pass to `monovm_initialize`. The MSBuild task will check that `runtimeconfig.json` does not set any of those same properties. If there is a duplicate, error out. + +All properties set in either the `runtimeconfig.json` or set via `monovm_initialize` will be propagated to the managed `AppContext`. Mono will also check for properties it supports in the runtime itself and make use of them as appropriate. + +## Design Details + +### Encoded file generation + + The runtime pack will provide an MSBuild task called RuntimeConfigParserTask to generate the encoded file. The generator checks for duplicate property keys (by comparing the keys in the json file with an input list of properties that the embedder promises to pass to `monovm_initialize`). + +#### Task Contract: + +The task will take 3 input arguments: +1. The path to the `runtimeconfig.json` file. +2. The name of the destination file to be written in the encoded format. +3. An item list (`ITaskItem[]`) that is the name of the properties that the embedder will set on `monovm_initialize`. + +The task should: +1. Parse the given input file and create a dictionary from the configProperties key. +2. Compare the keys from the input file with the names given in the item list. If there are any duplicates, return an MSBuild Error. +3. Generate the output file. + +#### Example of the usage of the task: + + ``` + + + + + + + ``` + +### The encoded runtimeconfig format + +The format is: +1. There will be an (1- to 4-byte) ECMA-335 II.23.2 compressed unsigned integer count = N, indicating the number of key-value pairs. +2. It will have 2xN ECMA-335 SerString UTF8 strings (that is, each string is preceded by its length stored in the compressed unsigned integer format): Each key followed by its value. + +Sample input +``` +{ + "runtimeOptions": { + "configProperties": { + "key1": "value1", + "key2": "value2" + } + } +} +``` + +Sample output (as hexdump -C bytes) +``` +00000000 02 04 6b 65 79 31 06 76 61 6c 75 65 31 04 6b 65 |..key1.value1.ke| +00000010 79 32 06 76 61 6c 75 65 32 |y2.value2| +00000019 +``` + +### New embedding API entrypoint + +We want at least 2 ways to pass data in: either ask the runtime to open the file, or give it a pointer to the data in memory. Also, we need some way to cleanup. + +``` +struct MonovmRuntimeConfigArguments { + uint32_t kind; // 0 = Path of runtimeconfig.bin file, 1 = pointer to the blob data, >= 2 reserved + union { + struct { + const char *path; // null terminated absolute path + } name; + struct { + const char *data; + uint32_t data_len; + } data; + } runtimeconfig; +}; + +typedef void (*MonovmRuntimeConfigArgumentsCleanup)(MonovmRuntimeConfigArguments *args, void* user_data); + +MONO_API void +monovm_runtimeconfig_initialize (MonovmRuntimeConfigArguments *args, MonovmRuntimeConfigArgumentsCleanup cleanup_fn, void* user_data); +``` + +This declaration should live in the unstable header. `monovm_runtimeconfig_initialize` should be called before `monovm_initialize`. + +#### Example of the usage of `monovm_runtimeconfig_initialize` + +``` +void +cleanup_runtime_config (MonovmRuntimeConfigArguments *args, void *user_data) +{ + free (args); + free (user_data); // This may not be needed, depending on if there is anything needs to be freed. +} + +int +mono_droid_runtime_init (const char* executable, int managed_argc, char* managed_argv[]) +{ + + ...... + + MonovmRuntimeConfigArguments *arg = (MonovmRuntimeConfigArguments *)malloc (sizeof (MonovmRuntimeConfigArguments)); + arg->kind = 0; + arg->runtimeconfig.name.path = "path_to_generated_binary_file"; + monovm_runtimeconfig_initialize (arg, cleanup_runtime_config, NULL); + + monovm_initialize(......); + ...... + +} +``` + +### Register and install runtime properties + +`monovm_runtimeconfig_initialize` will register the type `MonovmRuntimeConfigArguments` variable with the runtime. If given the path of the runtimeconfig.bin file, the runtime will read the binary data from the file, otherwise, it will parse the binary data from memory. The properties will be combined with the ones registered by `monovm_initialize` and used to initialize System.AppContext. + +### Cleanup function + +The `MonovmRuntimeConfigArguments*` will be stored in the runtime between `monovm_runtimeconfig_initialize` and `mono_jit_init_version`. The embedder should not dispose of the arguments after calling `monovm_runtimeconfig_initialize`. Instead the runtime will call the `cleanup_fn` that is passed to `monovm_runtimeconfig_initialize` at soon after it has initialized the managed property list in `System.AppContext` (which happens as part of `mono_jit_init_version`). diff --git a/docs/workflow/building/libraries/webassembly-instructions.md b/docs/workflow/building/libraries/webassembly-instructions.md index 315611d1ded9d2..42004629c15439 100644 --- a/docs/workflow/building/libraries/webassembly-instructions.md +++ b/docs/workflow/building/libraries/webassembly-instructions.md @@ -4,7 +4,10 @@ If you haven't already done so, please read [this document](../../README.md#Build_Requirements) to understand the build requirements for your operating system. -The Emscripten SDK (emsdk) needs to be installed. Follow the installation guide [here](https://emscripten.org/docs/getting_started/downloads.html#sdk-download-and-install) or run `make -C src/mono/wasm provision-wasm` to install emsdk into `src/mono/wasm/emsdk`. +The **correct version** of Emscripten SDK (emsdk) needs to be installed. +* Run `make -C src/mono/wasm provision-wasm` to install emsdk into `src/mono/wasm/emsdk`. +* Alternatively follow the [installation guide](https://emscripten.org/docs/getting_started/downloads.html#sdk-download-and-install). +Do not install `latest` but rather specific version e.g. `./emsdk install 2.0.12`. See [emscripten-version.txt](..\..\..\..\src\mono\wasm\emscripten-version.txt) Once installed the `EMSDK_PATH` environment variable needs to be set: diff --git a/docs/workflow/testing/libraries/filtering-tests.md b/docs/workflow/testing/libraries/filtering-tests.md index 84fc56997b4fb6..96beabad398cd9 100644 --- a/docs/workflow/testing/libraries/filtering-tests.md +++ b/docs/workflow/testing/libraries/filtering-tests.md @@ -68,6 +68,24 @@ This attribute returns the 'failing' category, which is disabled by default. ``` Use this attribute over test methods to skip failing tests only on the specific platforms and the specific target frameworks. +#### SkipOnPlatformAttribute +This attribute is intended to disable a test permanently on a platform where an API is not available or there is an intentional difference in behavior in between the tested platform and the skipped platform. + +This attribute can be applied either to a test assembly/class (will disable all the tests in that assembly/class) or to a test method. It allows multiple usages on the same member. + +```cs +[SkipOnPlatform(TestPlatforms platforms, string reason)] +``` + +Use this attribute over test methods to skip tests only on the specific target platforms. The reason parameter doesn't affect the traits but we rather always use it so that when we see this attribute we know why it is being skipped on that platform. + +If it needs to be skipped in multiple platforms and the reasons are different please use two attributes on the same test so that you can specify different reasons for each platform. + +When you add the attribute on the whole test assembly it's a good idea to also add `true` to the test .csproj. +That allows the CI build to skip sending this test assembly to Helix completely since it'd run zero tests anyway. + +**Currently these are the [Test Platforms](https://github.com/dotnet/arcade/blob/master/src/Microsoft.DotNet.XUnitExtensions/src/TestPlatforms.cs) that we support through our test execution infrastructure** + #### SkipOnTargetFrameworkAttribute This attribute is intended to disable a test permanently on a framework where an API is not available or there is an intentional difference in behavior in between the tested framework and the skipped framework. @@ -80,7 +98,7 @@ Use this attribute over test methods to skip tests only on the specific target f If it needs to be skipped in multiple frameworks and the reasons are different please use two attributes on the same test so that you can specify different reasons for each framework. -**Currently this are the [Framework Monikers](https://github.com/dotnet/arcade/blob/master/src/Microsoft.DotNet.XUnitExtensions/src/TargetFrameworkMonikers.cs#L23-L26) that we support through our test execution infrastructure** +**Currently these are the [Framework Monikers](https://github.com/dotnet/arcade/blob/master/src/Microsoft.DotNet.XUnitExtensions/src/TargetFrameworkMonikers.cs#L23-L26) that we support through our test execution infrastructure** #### ConditionalFactAttribute Use this attribute to run the test only when a condition is `true`. This attribute is used when `ActiveIssueAttribute` or `SkipOnTargetFrameworkAttribute` are not flexible enough due to needing to run a custom logic at test time. This test behaves as a `[Fact]` test that has no test data passed in as a parameter. diff --git a/docs/workflow/testing/libraries/testing-wasm.md b/docs/workflow/testing/libraries/testing-wasm.md index 242e6c9cee4a90..f060a00f9b4588 100644 --- a/docs/workflow/testing/libraries/testing-wasm.md +++ b/docs/workflow/testing/libraries/testing-wasm.md @@ -144,6 +144,12 @@ At the moment supported values are: By default, `chrome` browser is used. +## Debugging + +### Getting more information + +- Line numbers: add `/p:DebuggerSupport=true` to the command line, for `Release` builds. It's enabled by default for `Debug` builds. + ## Kicking off outer loop tests from GitHub Interface Add the following to the comment of a PR. diff --git a/docs/workflow/testing/libraries/testing.md b/docs/workflow/testing/libraries/testing.md index 9ab30e12be41ad..2182a959a2f8ee 100644 --- a/docs/workflow/testing/libraries/testing.md +++ b/docs/workflow/testing/libraries/testing.md @@ -1,6 +1,6 @@ # Testing Libraries -We use the OSS testing framework [xunit](http://xunit.github.io/). +We use the OSS testing framework [xunit](https://github.com/xunit/xunit). To build the tests and run them you can call the libraries build script. diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 33d6d5e04e6127..f3150e21229986 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,74 +1,74 @@ - + https://github.com/dotnet/icu - 29647ace51f6bb8085326ff137525f7a6d89d726 + 8d8a9f44bf31bc0cb039619e508e46cf85c08768 - + https://github.com/dotnet/arcade - 1bfe91238cb39b9620b878b8f1bf0c789324b4cd + 28d9452d7e2ae4e98a1df735b90b03d3cac1f4e7 - + https://github.com/dotnet/arcade - 1bfe91238cb39b9620b878b8f1bf0c789324b4cd + 28d9452d7e2ae4e98a1df735b90b03d3cac1f4e7 - + https://github.com/dotnet/arcade - 1bfe91238cb39b9620b878b8f1bf0c789324b4cd + 28d9452d7e2ae4e98a1df735b90b03d3cac1f4e7 - + https://github.com/dotnet/arcade - 1bfe91238cb39b9620b878b8f1bf0c789324b4cd + 28d9452d7e2ae4e98a1df735b90b03d3cac1f4e7 - + https://github.com/dotnet/arcade - 1bfe91238cb39b9620b878b8f1bf0c789324b4cd + 28d9452d7e2ae4e98a1df735b90b03d3cac1f4e7 - + https://github.com/dotnet/arcade - 1bfe91238cb39b9620b878b8f1bf0c789324b4cd + 28d9452d7e2ae4e98a1df735b90b03d3cac1f4e7 - + https://github.com/dotnet/arcade - 1bfe91238cb39b9620b878b8f1bf0c789324b4cd + 28d9452d7e2ae4e98a1df735b90b03d3cac1f4e7 - + https://github.com/dotnet/arcade - 1bfe91238cb39b9620b878b8f1bf0c789324b4cd + 28d9452d7e2ae4e98a1df735b90b03d3cac1f4e7 - + https://github.com/dotnet/arcade - 1bfe91238cb39b9620b878b8f1bf0c789324b4cd + 28d9452d7e2ae4e98a1df735b90b03d3cac1f4e7 - + https://github.com/dotnet/arcade - 1bfe91238cb39b9620b878b8f1bf0c789324b4cd + 28d9452d7e2ae4e98a1df735b90b03d3cac1f4e7 - + https://github.com/dotnet/arcade - 1bfe91238cb39b9620b878b8f1bf0c789324b4cd + 28d9452d7e2ae4e98a1df735b90b03d3cac1f4e7 - + https://github.com/dotnet/arcade - 1bfe91238cb39b9620b878b8f1bf0c789324b4cd + 28d9452d7e2ae4e98a1df735b90b03d3cac1f4e7 - + https://github.com/dotnet/arcade - 1bfe91238cb39b9620b878b8f1bf0c789324b4cd + 28d9452d7e2ae4e98a1df735b90b03d3cac1f4e7 - + https://github.com/dotnet/arcade - 1bfe91238cb39b9620b878b8f1bf0c789324b4cd + 28d9452d7e2ae4e98a1df735b90b03d3cac1f4e7 - + https://github.com/dotnet/arcade - 1bfe91238cb39b9620b878b8f1bf0c789324b4cd + 28d9452d7e2ae4e98a1df735b90b03d3cac1f4e7 - + https://github.com/dotnet/arcade - 1bfe91238cb39b9620b878b8f1bf0c789324b4cd + 28d9452d7e2ae4e98a1df735b90b03d3cac1f4e7 https://dev.azure.com/dnceng/internal/_git/dotnet-optimization @@ -94,121 +94,121 @@ https://github.com/microsoft/vstest 140434f7109d357d0158ade9e5164a4861513965 - + https://github.com/dotnet/runtime-assets - 055ed026132a7070e41629cfb5e410f0fcbdf948 + f071377c4c5db727bb65abc878877ccf89f67ac1 - + https://github.com/dotnet/runtime-assets - 055ed026132a7070e41629cfb5e410f0fcbdf948 + f071377c4c5db727bb65abc878877ccf89f67ac1 - + https://github.com/dotnet/runtime-assets - 055ed026132a7070e41629cfb5e410f0fcbdf948 + f071377c4c5db727bb65abc878877ccf89f67ac1 - + https://github.com/dotnet/runtime-assets - 055ed026132a7070e41629cfb5e410f0fcbdf948 + f071377c4c5db727bb65abc878877ccf89f67ac1 - + https://github.com/dotnet/runtime-assets - 055ed026132a7070e41629cfb5e410f0fcbdf948 + f071377c4c5db727bb65abc878877ccf89f67ac1 - + https://github.com/dotnet/runtime-assets - 055ed026132a7070e41629cfb5e410f0fcbdf948 + f071377c4c5db727bb65abc878877ccf89f67ac1 - + https://github.com/dotnet/runtime-assets - 055ed026132a7070e41629cfb5e410f0fcbdf948 + f071377c4c5db727bb65abc878877ccf89f67ac1 - + https://github.com/dotnet/runtime-assets - 055ed026132a7070e41629cfb5e410f0fcbdf948 + f071377c4c5db727bb65abc878877ccf89f67ac1 - + https://github.com/dotnet/runtime-assets - 055ed026132a7070e41629cfb5e410f0fcbdf948 + f071377c4c5db727bb65abc878877ccf89f67ac1 - + https://github.com/dotnet/llvm-project - a2bf9755287bdd3dcf998e41042e79dfd0136c85 + 389fd50958bf18627d83fa63ab6d627a9fe82042 - + https://github.com/dotnet/llvm-project - a2bf9755287bdd3dcf998e41042e79dfd0136c85 + 389fd50958bf18627d83fa63ab6d627a9fe82042 - + https://github.com/dotnet/llvm-project - a2bf9755287bdd3dcf998e41042e79dfd0136c85 + 389fd50958bf18627d83fa63ab6d627a9fe82042 - + https://github.com/dotnet/llvm-project - a2bf9755287bdd3dcf998e41042e79dfd0136c85 + 389fd50958bf18627d83fa63ab6d627a9fe82042 - + https://github.com/dotnet/llvm-project - a2bf9755287bdd3dcf998e41042e79dfd0136c85 + 389fd50958bf18627d83fa63ab6d627a9fe82042 - + https://github.com/dotnet/llvm-project - a2bf9755287bdd3dcf998e41042e79dfd0136c85 + 389fd50958bf18627d83fa63ab6d627a9fe82042 - + https://github.com/dotnet/llvm-project - a2bf9755287bdd3dcf998e41042e79dfd0136c85 + 389fd50958bf18627d83fa63ab6d627a9fe82042 - + https://github.com/dotnet/llvm-project - a2bf9755287bdd3dcf998e41042e79dfd0136c85 + 389fd50958bf18627d83fa63ab6d627a9fe82042 https://github.com/dotnet/runtime 38017c3935de95d0335bac04f4901ddfc2718656 - + https://github.com/dotnet/runtime - 102d1e856c7e0e553abeec937783da5debed73ad + 355eff52bed00e7ca9d4a6d769ddbe2bbadbea47 - + https://github.com/dotnet/runtime - 102d1e856c7e0e553abeec937783da5debed73ad + 355eff52bed00e7ca9d4a6d769ddbe2bbadbea47 - + https://github.com/dotnet/runtime - 102d1e856c7e0e553abeec937783da5debed73ad + 355eff52bed00e7ca9d4a6d769ddbe2bbadbea47 - + https://github.com/dotnet/runtime - 102d1e856c7e0e553abeec937783da5debed73ad + 355eff52bed00e7ca9d4a6d769ddbe2bbadbea47 - + https://github.com/dotnet/runtime - 102d1e856c7e0e553abeec937783da5debed73ad + 355eff52bed00e7ca9d4a6d769ddbe2bbadbea47 - + https://github.com/dotnet/runtime - 102d1e856c7e0e553abeec937783da5debed73ad + 355eff52bed00e7ca9d4a6d769ddbe2bbadbea47 - + https://github.com/dotnet/runtime - 102d1e856c7e0e553abeec937783da5debed73ad + 355eff52bed00e7ca9d4a6d769ddbe2bbadbea47 https://github.com/mono/linker 388fef00320370785b08bbc42dda4a87f3fa38ab - + https://github.com/dotnet/xharness - c2c34bf7fdeb5a89e83817ced9a1a2c3c4cfc15c + b2297d610df1ae15fc7ba8bd8c9bc0a7192aaefa - + https://github.com/dotnet/xharness - c2c34bf7fdeb5a89e83817ced9a1a2c3c4cfc15c + b2297d610df1ae15fc7ba8bd8c9bc0a7192aaefa - + https://github.com/dotnet/arcade - 1bfe91238cb39b9620b878b8f1bf0c789324b4cd + 28d9452d7e2ae4e98a1df735b90b03d3cac1f4e7 diff --git a/eng/Versions.props b/eng/Versions.props index 16f6260879c6e3..fd8262eb0e5173 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -49,28 +49,28 @@ 3.9.0-5.final 3.9.0-5.final - 6.0.0-beta.21203.1 - 6.0.0-beta.21203.1 - 6.0.0-beta.21203.1 - 6.0.0-beta.21203.1 - 6.0.0-beta.21203.1 - 6.0.0-beta.21203.1 - 2.5.1-beta.21203.1 - 6.0.0-beta.21203.1 - 6.0.0-beta.21203.1 - 6.0.0-beta.21203.1 - 6.0.0-beta.21203.1 - 6.0.0-beta.21203.1 - 6.0.0-beta.21203.1 + 6.0.0-beta.21209.17 + 6.0.0-beta.21209.17 + 6.0.0-beta.21209.17 + 6.0.0-beta.21209.17 + 6.0.0-beta.21209.17 + 6.0.0-beta.21209.17 + 2.5.1-beta.21209.17 + 6.0.0-beta.21209.17 + 6.0.0-beta.21209.17 + 6.0.0-beta.21209.17 + 6.0.0-beta.21209.17 + 6.0.0-beta.21209.17 + 6.0.0-beta.21209.17 5.9.0-preview.2 6.0.0-alpha.1.20612.4 - 6.0.0-preview.4.21178.6 - 6.0.0-preview.4.21178.6 + 6.0.0-preview.4.21211.7 + 6.0.0-preview.4.21211.7 3.1.0 - 6.0.0-preview.4.21178.6 + 6.0.0-preview.4.21211.7 1.2.0-beta.304 4.5.1 @@ -98,24 +98,24 @@ 4.7.0 4.7.0 4.7.0 - 6.0.0-preview.4.21178.6 - 6.0.0-preview.4.21178.6 + 6.0.0-preview.4.21211.7 + 6.0.0-preview.4.21211.7 4.3.0 4.5.4 4.5.0 1.1.1 4.3.0 - 6.0.0-preview.4.21178.6 + 6.0.0-preview.4.21211.7 - 6.0.0-beta.21174.2 - 6.0.0-beta.21174.2 - 6.0.0-beta.21174.2 - 6.0.0-beta.21174.2 - 6.0.0-beta.21174.2 - 6.0.0-beta.21174.2 - 6.0.0-beta.21174.2 - 6.0.0-beta.21174.2 - 6.0.0-beta.21174.2 + 6.0.0-beta.21212.1 + 6.0.0-beta.21212.1 + 6.0.0-beta.21212.1 + 6.0.0-beta.21212.1 + 6.0.0-beta.21212.1 + 6.0.0-beta.21212.1 + 6.0.0-beta.21212.1 + 6.0.0-beta.21212.1 + 6.0.0-beta.21212.1 99.99.99-master-20210317.2 99.99.99-master-20210317.2 @@ -148,8 +148,8 @@ 1.0.1-prerelease-00006 16.9.0-preview-20201201-01 - 1.0.0-prerelease.21201.1 - 1.0.0-prerelease.21201.1 + 1.0.0-prerelease.21209.2 + 1.0.0-prerelease.21209.2 2.4.1 2.4.2 1.3.0 @@ -161,16 +161,16 @@ 6.0.100-preview.2.21205.2 - 6.0.0-preview.4.21179.1 + 6.0.0-preview.4.21205.1 - 9.0.1-alpha.1.21179.2 - 9.0.1-alpha.1.21179.2 - 9.0.1-alpha.1.21179.2 - 9.0.1-alpha.1.21179.2 - 9.0.1-alpha.1.21179.2 - 9.0.1-alpha.1.21179.2 - 9.0.1-alpha.1.21179.2 - 9.0.1-alpha.1.21179.2 + 9.0.1-alpha.1.21205.1 + 9.0.1-alpha.1.21205.1 + 9.0.1-alpha.1.21205.1 + 9.0.1-alpha.1.21205.1 + 9.0.1-alpha.1.21205.1 + 9.0.1-alpha.1.21205.1 + 9.0.1-alpha.1.21205.1 + 9.0.1-alpha.1.21205.1 diff --git a/eng/common/cross/build-android-rootfs.sh b/eng/common/cross/build-android-rootfs.sh index 42516bbeebc3f4..c29c8267e7a4c2 100755 --- a/eng/common/cross/build-android-rootfs.sh +++ b/eng/common/cross/build-android-rootfs.sh @@ -106,6 +106,7 @@ __AndroidPackages+=" libandroid-glob" __AndroidPackages+=" liblzma" __AndroidPackages+=" krb5" __AndroidPackages+=" openssl" +__AndroidPackages+=" openldap" for path in $(wget -qO- http://termux.net/dists/stable/main/binary-$__AndroidArch/Packages |\ grep -A15 "Package: \(${__AndroidPackages// /\\|}\)" | grep -v "static\|tool" | grep Filename); do diff --git a/eng/common/cross/build-rootfs.sh b/eng/common/cross/build-rootfs.sh index b26622444f5ba1..81e641a57b53d8 100755 --- a/eng/common/cross/build-rootfs.sh +++ b/eng/common/cross/build-rootfs.sh @@ -55,11 +55,13 @@ __UbuntuPackages+=" libcurl4-openssl-dev" __UbuntuPackages+=" libkrb5-dev" __UbuntuPackages+=" libssl-dev" __UbuntuPackages+=" zlib1g-dev" +__UbuntuPackages+=" libldap2-dev" __AlpinePackages+=" curl-dev" __AlpinePackages+=" krb5-dev" __AlpinePackages+=" openssl-dev" __AlpinePackages+=" zlib-dev" +__AlpinePackages+=" openldap-dev" __FreeBSDBase="12.1-RELEASE" __FreeBSDPkg="1.12.0" @@ -68,11 +70,13 @@ __FreeBSDPackages+=" icu" __FreeBSDPackages+=" libinotify" __FreeBSDPackages+=" lttng-ust" __FreeBSDPackages+=" krb5" +__FreeBSDPackages+=" libslapi-2.4" __IllumosPackages="icu-64.2nb2" __IllumosPackages+=" mit-krb5-1.16.2nb4" __IllumosPackages+=" openssl-1.1.1e" __IllumosPackages+=" zlib-1.2.11" +__IllumosPackages+=" openldap-client-2.4.49" __UseMirror=0 diff --git a/eng/common/templates/job/performance.yml b/eng/common/templates/job/performance.yml deleted file mode 100644 index e6755df2234295..00000000000000 --- a/eng/common/templates/job/performance.yml +++ /dev/null @@ -1,95 +0,0 @@ -parameters: - steps: [] # optional -- any additional steps that need to happen before pulling down the performance repo and sending the performance benchmarks to helix (ie building your repo) - variables: [] # optional -- list of additional variables to send to the template - jobName: '' # required -- job name - displayName: '' # optional -- display name for the job. Will use jobName if not passed - pool: '' # required -- name of the Build pool - container: '' # required -- name of the container - osGroup: '' # required -- operating system for the job - extraSetupParameters: '' # optional -- extra arguments to pass to the setup script - frameworks: ['netcoreapp3.0'] # optional -- list of frameworks to run against - continueOnError: 'false' # optional -- determines whether to continue the build if the step errors - dependsOn: '' # optional -- dependencies of the job - timeoutInMinutes: 320 # optional -- timeout for the job - enableTelemetry: false # optional -- enable for telemetry - -jobs: -- template: ../jobs/jobs.yml - parameters: - dependsOn: ${{ parameters.dependsOn }} - enableTelemetry: ${{ parameters.enableTelemetry }} - enablePublishBuildArtifacts: true - continueOnError: ${{ parameters.continueOnError }} - - jobs: - - job: '${{ parameters.jobName }}' - - ${{ if ne(parameters.displayName, '') }}: - displayName: '${{ parameters.displayName }}' - ${{ if eq(parameters.displayName, '') }}: - displayName: '${{ parameters.jobName }}' - - timeoutInMinutes: ${{ parameters.timeoutInMinutes }} - - variables: - - - ${{ each variable in parameters.variables }}: - - ${{ if ne(variable.name, '') }}: - - name: ${{ variable.name }} - value: ${{ variable.value }} - - ${{ if ne(variable.group, '') }}: - - group: ${{ variable.group }} - - - IsInternal: '' - - HelixApiAccessToken: '' - - HelixPreCommand: '' - - - ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - ${{ if eq( parameters.osGroup, 'Windows_NT') }}: - - HelixPreCommand: 'set "PERFLAB_UPLOAD_TOKEN=$(PerfCommandUploadToken)"' - - IsInternal: -Internal - - ${{ if ne(parameters.osGroup, 'Windows_NT') }}: - - HelixPreCommand: 'export PERFLAB_UPLOAD_TOKEN="$(PerfCommandUploadTokenLinux)"' - - IsInternal: --internal - - - group: DotNet-HelixApi-Access - - group: dotnet-benchview - - workspace: - clean: all - pool: - ${{ parameters.pool }} - container: ${{ parameters.container }} - strategy: - matrix: - ${{ each framework in parameters.frameworks }}: - ${{ framework }}: - _Framework: ${{ framework }} - steps: - - checkout: self - clean: true - # Run all of the steps to setup repo - - ${{ each step in parameters.steps }}: - - ${{ step }} - - powershell: $(Build.SourcesDirectory)\eng\testing\performance\performance-setup.ps1 $(IsInternal) -Framework $(_Framework) ${{ parameters.extraSetupParameters }} - displayName: Performance Setup (Windows) - condition: and(succeeded(), eq(variables['Agent.Os'], 'Windows_NT')) - continueOnError: ${{ parameters.continueOnError }} - - script: $(Build.SourcesDirectory)/eng/testing/performance/performance-setup.sh $(IsInternal) --framework $(_Framework) ${{ parameters.extraSetupParameters }} - displayName: Performance Setup (Unix) - condition: and(succeeded(), ne(variables['Agent.Os'], 'Windows_NT')) - continueOnError: ${{ parameters.continueOnError }} - - script: $(Python) $(PerformanceDirectory)/scripts/ci_setup.py $(SetupArguments) - displayName: Run ci setup script - # Run perf testing in helix - - template: /eng/common/templates/steps/perf-send-to-helix.yml - parameters: - HelixSource: '$(HelixSourcePrefix)/$(Build.Repository.Name)/$(Build.SourceBranch)' # sources must start with pr/, official/, prodcon/, or agent/ - HelixType: 'test/performance/$(Kind)/$(_Framework)/$(Architecture)' - HelixAccessToken: $(HelixApiAccessToken) - HelixTargetQueues: $(Queue) - HelixPreCommands: $(HelixPreCommand) - Creator: $(Creator) - WorkItemTimeout: 4:00 # 4 hours - WorkItemDirectory: '$(WorkItemDirectory)' # WorkItemDirectory can not be empty, so we send it some docs to keep it happy - CorrelationPayloadDirectory: '$(PayloadDirectory)' # it gets checked out to a folder with shorter path than WorkItemDirectory so we can avoid file name too long exceptions \ No newline at end of file diff --git a/eng/native/gen-buildsys.cmd b/eng/native/gen-buildsys.cmd index 2d81dd174e0a00..740102f6f2d684 100644 --- a/eng/native/gen-buildsys.cmd +++ b/eng/native/gen-buildsys.cmd @@ -76,7 +76,7 @@ if not "%__ConfigureOnly%" == "1" ( if exist "%__CmdLineOptionsUpToDateFile%" ( set /p __CMakeCmdLineCache=<"%__CmdLineOptionsUpToDateFile%" REM Strip the extra space from the end of the cached command line - if [!__ExtraCmakeParams!] == [!__CMakeCmdLineCache:~0,-1!] ( + if "!__ExtraCmakeParams!" == "!__CMakeCmdLineCache:~0,-1!" ( echo The CMake command line is the same as the last run. Skipping running CMake. exit /B 0 ) else ( diff --git a/eng/pipelines/common/global-build-job.yml b/eng/pipelines/common/global-build-job.yml index 126cecfacaf05b..2b4b08a9d80784 100644 --- a/eng/pipelines/common/global-build-job.yml +++ b/eng/pipelines/common/global-build-job.yml @@ -19,6 +19,7 @@ parameters: shouldContinueOnError: false dependOnEvaluatePaths: false isOfficialBuild: false + buildingOnSourceBuildImage: false runtimeFlavor: 'coreclr' helixQueues: '' enablePublishTestResults: false @@ -51,7 +52,7 @@ jobs: dependsOn: ${{ parameters.dependsOn }} ${{ if eq(parameters.dependOnEvaluatePaths, true) }}: - dependsOn: + dependsOn: - evaluate_paths - ${{ if ne(parameters.dependsOn,'') }}: - ${{ parameters.dependsOn }} @@ -59,7 +60,7 @@ jobs: variables: - name: _osParameter value: -os ${{ parameters.osGroup }} - + - ${{ if and(eq(parameters.osGroup, 'Linux'), eq(parameters.osSubGroup, '_musl')) }}: - name: _osParameter value: /p:RuntimeOS=linux-musl /p:OutputRid=linux-musl-${{ parameters.archType }} @@ -79,13 +80,20 @@ jobs: value: /p:OfficialBuildId=$(Build.BuildNumber) ${{ if ne(parameters.isOfficialBuild, true) }}: value: '' - + - name: _richCodeNavigationParam ${{ if eq(parameters.enableRichCodeNavigation, true) }}: value: /p:EnableRichCodeNavigation=true ${{ if ne(parameters.enableRichCodeNavigation, true) }}: value: '' + - name: _sclEnableCommand + ${{ if eq(parameters.buildingOnSourceBuildImage, true) }}: + value: scl enable llvm-toolset-7.0 -- + ${{ if ne(parameters.buildingOnSourceBuildImage, true) }}: + value: '' + + - ${{ each variable in parameters.variables }}: - ${{ variable }} @@ -124,7 +132,7 @@ jobs: displayName: Install native dependencies # Build - - script: $(Build.SourcesDirectory)$(dir)build$(scriptExt) -ci -arch ${{ parameters.archType }} $(_osParameter) ${{ parameters.buildArgs }} $(_officialBuildParameter) $(_crossBuildPropertyArg) $(_cxx11Parameter) $(_richCodeNavigationParam) + - script: $(_sclEnableCommand) $(Build.SourcesDirectory)$(dir)build$(scriptExt) -ci -arch ${{ parameters.archType }} $(_osParameter) ${{ parameters.buildArgs }} $(_officialBuildParameter) $(_crossBuildPropertyArg) $(_cxx11Parameter) $(_richCodeNavigationParam) displayName: Build product ${{ if eq(parameters.useContinueOnErrorDuringBuild, true) }}: continueOnError: ${{ parameters.shouldContinueOnError }} diff --git a/eng/pipelines/common/platform-matrix.yml b/eng/pipelines/common/platform-matrix.yml index 000d1dcecdfdac..ea17c5643461f3 100644 --- a/eng/pipelines/common/platform-matrix.yml +++ b/eng/pipelines/common/platform-matrix.yml @@ -180,6 +180,31 @@ jobs: helixQueueGroup: ${{ parameters.helixQueueGroup }} ${{ insert }}: ${{ parameters.jobParameters }} +# Linux x64 Source Build + +- ${{ if containsValue(parameters.platforms, 'SourceBuild_Linux_x64') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: Linux + archType: x64 + targetRid: linux-x64 + platform: Linux_x64 + container: + image: centos-7-source-build-20210408124356-5d87b80 + registry: mcr + jobParameters: + runtimeFlavor: ${{ parameters.runtimeFlavor }} + stagedBuild: ${{ parameters.stagedBuild }} + buildConfig: ${{ parameters.buildConfig }} + ${{ if eq(parameters.passPlatforms, true) }}: + platforms: ${{ parameters.platforms }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + ${{ insert }}: ${{ parameters.jobParameters }} + buildingOnSourceBuildImage: true + # WebAssembly - ${{ if containsValue(parameters.platforms, 'Browser_wasm') }}: diff --git a/eng/pipelines/global-build.yml b/eng/pipelines/global-build.yml index 3a737e69c52131..b415c5e5cac57b 100644 --- a/eng/pipelines/global-build.yml +++ b/eng/pipelines/global-build.yml @@ -130,8 +130,8 @@ jobs: jobTemplate: /eng/pipelines/common/global-build-job.yml buildConfig: release platforms: - - Linux_x64 + - SourceBuild_Linux_x64 jobParameters: nameSuffix: SourceBuild - buildArgs: /p:DotNetBuildFromSource=true + buildArgs: -subset clr+libs+host+packs /p:DotNetBuildFromSource=true timeoutInMinutes: 90 diff --git a/eng/testing/AppleRunnerTemplate.sh b/eng/testing/AppleRunnerTemplate.sh index 0f2eb7f2abed87..47352018d1ac42 100644 --- a/eng/testing/AppleRunnerTemplate.sh +++ b/eng/testing/AppleRunnerTemplate.sh @@ -16,17 +16,17 @@ if [ -n "$5" ]; then ADDITIONAL_ARGS=${@:5} fi -if [[ "$TARGET_OS" == "MacCatalyst" ]]; then TARGET=maccatalyst; fi +if [[ "$TARGET_OS" == "maccatalyst" ]]; then TARGET=maccatalyst; fi -if [[ "$TARGET_OS" == "iOSSimulator" && "$TARGET_ARCH" == "x86" ]]; then TARGET=ios-simulator-32; fi -if [[ "$TARGET_OS" == "iOSSimulator" && "$TARGET_ARCH" == "x64" ]]; then TARGET=ios-simulator-64; fi -if [[ "$TARGET_OS" == "iOSSimulator" && "$TARGET_ARCH" == "arm64" ]]; then TARGET=ios-simulator-64; fi -if [[ "$TARGET_OS" == "iOS" && "$TARGET_ARCH" == "arm" ]]; then TARGET=ios-device; fi -if [[ "$TARGET_OS" == "iOS" && "$TARGET_ARCH" == "arm64" ]]; then TARGET=ios-device; fi +if [[ "$TARGET_OS" == "iossimulator" && "$TARGET_ARCH" == "x86" ]]; then TARGET=ios-simulator-32; fi +if [[ "$TARGET_OS" == "iossimulator" && "$TARGET_ARCH" == "x64" ]]; then TARGET=ios-simulator-64; fi +if [[ "$TARGET_OS" == "iossimulator" && "$TARGET_ARCH" == "arm64" ]]; then TARGET=ios-simulator-64; fi +if [[ "$TARGET_OS" == "ios" && "$TARGET_ARCH" == "arm" ]]; then TARGET=ios-device; fi +if [[ "$TARGET_OS" == "ios" && "$TARGET_ARCH" == "arm64" ]]; then TARGET=ios-device; fi -if [[ "$TARGET_OS" == "tvOSSimulator" && "$TARGET_ARCH" == "x64" ]]; then TARGET=tvos-simulator; fi -if [[ "$TARGET_OS" == "tvOSSimulator" && "$TARGET_ARCH" == "arm64" ]]; then TARGET=tvos-simulator; fi -if [[ "$TARGET_OS" == "tvOS" && "$TARGET_ARCH" == "arm64" ]]; then TARGET=tvos-device; fi +if [[ "$TARGET_OS" == "tvossimulator" && "$TARGET_ARCH" == "x64" ]]; then TARGET=tvos-simulator; fi +if [[ "$TARGET_OS" == "tvossimulator" && "$TARGET_ARCH" == "arm64" ]]; then TARGET=tvos-simulator; fi +if [[ "$TARGET_OS" == "tvos" && "$TARGET_ARCH" == "arm64" ]]; then TARGET=tvos-device; fi # "Release" in SCHEME_SDK is what xcode produces (see "bool Optimized" property in AppleAppBuilderTask) if [[ "$TARGET" == "ios-simulator-"* ]]; then SCHEME_SDK=Release-iphonesimulator; fi diff --git a/eng/testing/RunnerTemplate.cmd b/eng/testing/RunnerTemplate.cmd index 9e6c6d1e7866bf..0d898b4aa182ad 100644 --- a/eng/testing/RunnerTemplate.cmd +++ b/eng/testing/RunnerTemplate.cmd @@ -59,10 +59,11 @@ echo ----- end %DATE% %TIME% ----- exit code %ERRORLEVEL% ---------------------- :: The helix work item should not exit with non-zero if tests ran and produced results :: The special console runner for runtime returns 1 when tests fail if %ERRORLEVEL%==1 ( - exit /b 0 -) else ( - exit /b %ERRORLEVEL% + if not "%HELIX_WORKITEM_PAYLOAD%"=="" ( + exit /b 0 + ) ) +exit /b %ERRORLEVEL% :: ========================= END Test Execution ================================= :usage diff --git a/eng/testing/RunnerTemplate.sh b/eng/testing/RunnerTemplate.sh index a8bdcb1d478849..4770323fdc9c7d 100644 --- a/eng/testing/RunnerTemplate.sh +++ b/eng/testing/RunnerTemplate.sh @@ -225,8 +225,11 @@ popd >/dev/null # The helix work item should not exit with non-zero if tests ran and produced results # The special console runner for runtime returns 1 when tests fail if [ "$test_exitcode" == "1" ]; then - exit 0 -else - exit $test_exitcode + if [ -n "$HELIX_WORKITEM_PAYLOAD" ]; then + exit 0 + fi +fi + +exit $test_exitcode fi diff --git a/eng/testing/tests.targets b/eng/testing/tests.targets index 9fc08ddd6bcc70..53256725fb9594 100644 --- a/eng/testing/tests.targets +++ b/eng/testing/tests.targets @@ -96,7 +96,7 @@ $(RunTestsCommand) --runtime-path "$(TestHostRootPath.TrimEnd('\/'))" $(RunTestsCommand) --rsp-file "$(TestRspFile)" - "$(RunScriptOutputPath)" $(AssemblyName) $(TargetArchitecture) $(TargetOS) $(TestProjectName) $(AdditionalXHarnessArguments) + "$(RunScriptOutputPath)" $(AssemblyName) $(TargetArchitecture) $(TargetOS.ToLowerInvariant()) $(TestProjectName) $(AdditionalXHarnessArguments) "$(RunScriptOutputPath)" $(JSEngine) $(AssemblyName).dll $(Scenario) diff --git a/eng/testing/tests.wasm.targets b/eng/testing/tests.wasm.targets index 505b3a8bde603a..09311ffd96ef71 100644 --- a/eng/testing/tests.wasm.targets +++ b/eng/testing/tests.wasm.targets @@ -2,6 +2,7 @@ $(BundleTestAppTargets);BundleTestWasmApp + true @@ -27,7 +28,9 @@ true false false - false + + + false @@ -62,6 +65,9 @@ $(InvariantGlobalization) true false + + true + -1 @@ -79,4 +85,17 @@ + + + + + <_PdbFilesToCheck Include="$([System.IO.Path]::ChangeExtension('%(ResolvedFileToPublish.Identity)', '.pdb'))" + Condition="'%(ResolvedFileToPublish.Extension)' == '.dll'" /> + + + + diff --git a/global.json b/global.json index f2672dc65e4f2e..6a1e4ffb48d888 100644 --- a/global.json +++ b/global.json @@ -12,12 +12,12 @@ "python3": "3.7.1" }, "msbuild-sdks": { - "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21203.1", - "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21203.1", - "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21203.1", - "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21203.1", + "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21209.17", + "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21209.17", + "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21209.17", + "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21209.17", "Microsoft.Build.NoTargets": "2.0.17", "Microsoft.Build.Traversal": "2.1.1", - "Microsoft.NET.Sdk.IL": "6.0.0-preview.4.21178.6" + "Microsoft.NET.Sdk.IL": "6.0.0-preview.4.21211.7" } } diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index 5f0d5c2c940f87..d841ffc7287cbb 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -199,6 +199,7 @@ + diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicMethod.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicMethod.cs index 0721c3307bd186..336c1f0c986b4d 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicMethod.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicMethod.cs @@ -458,19 +458,15 @@ internal RuntimeMethodHandle GetMethodDescriptor() // if we are here we passed all the previous checks. Time to look at the arguments bool wrapExceptions = (invokeAttr & BindingFlags.DoNotWrapExceptions) == 0; - object retValue; - if (actualCount > 0) - { - object[] arguments = CheckArguments(parameters!, binder, invokeAttr, culture, sig); - retValue = RuntimeMethodHandle.InvokeMethod(null, arguments, sig, false, wrapExceptions); - // copy out. This should be made only if ByRef are present. - for (int index = 0; index < arguments.Length; index++) - parameters![index] = arguments[index]; - } - else - { - retValue = RuntimeMethodHandle.InvokeMethod(null, null, sig, false, wrapExceptions); - } + + StackAllocedArguments stackArgs = default; + Span arguments = CheckArguments(ref stackArgs, parameters, binder, invokeAttr, culture, sig); + object? retValue = RuntimeMethodHandle.InvokeMethod(null, arguments, sig, false, wrapExceptions); + + // copy out. This should be made only if ByRef are present. + // n.b. cannot use Span.CopyTo, as parameters.GetType() might not actually be typeof(object[]) + for (int index = 0; index < arguments.Length; index++) + parameters![index] = arguments[index]; GC.KeepAlive(this); return retValue; diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/RuntimeTypeMetadataUpdateHandler.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/RuntimeTypeMetadataUpdateHandler.cs new file mode 100644 index 00000000000000..25f29ed8e596eb --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/RuntimeTypeMetadataUpdateHandler.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Reflection.Metadata; + +[assembly: MetadataUpdateHandler(typeof(RuntimeTypeMetadataUpdateHandler))] + +namespace System.Reflection.Metadata +{ + /// Metadata update handler used to clear a Type's reflection cache in response to a metadata update notification. + internal static class RuntimeTypeMetadataUpdateHandler + { + public static void BeforeUpdate(Type? type) + { + if (type is RuntimeType rt) + { + rt.ClearCache(); + } + + // TODO: https://github.com/dotnet/runtime/issues/50938 + // Do we need to clear the cache on other types, e.g. ones derived from this one? + // Do we need to clear a cache on any other kinds of types? + } + } +} diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodBase.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodBase.CoreCLR.cs index 973b8564dc2c95..532d684605b0af 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodBase.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodBase.CoreCLR.cs @@ -1,8 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; using System.Globalization; +using System.Runtime.InteropServices; using System.Threading; +using Internal.Runtime.CompilerServices; namespace System.Reflection { @@ -62,30 +65,61 @@ internal virtual Type[] GetParameterTypes() return parameterTypes; } - internal object[] CheckArguments(object[] parameters, Binder? binder, + private protected Span CheckArguments(ref StackAllocedArguments stackArgs, object?[]? parameters, Binder? binder, BindingFlags invokeAttr, CultureInfo? culture, Signature sig) { - // copy the arguments in a different array so we detach from any user changes - object[] copyOfParameters = new object[parameters.Length]; + Debug.Assert(Unsafe.SizeOf() == StackAllocedArguments.MaxStackAllocArgCount * Unsafe.SizeOf(), + "MaxStackAllocArgCount not properly defined."); - ParameterInfo[]? p = null; - for (int i = 0; i < parameters.Length; i++) - { - object arg = parameters[i]; - RuntimeType argRT = sig.Arguments[i]; + Span copyOfParameters = default; - if (arg == Type.Missing) + if (parameters is not null) + { + // We need to perform type safety validation against the incoming arguments, but we also need + // to be resilient against the possibility that some other thread (or even the binder itself!) + // may mutate the array after we've validated the arguments but before we've properly invoked + // the method. The solution is to copy the arguments to a different, not-user-visible buffer + // as we validate them. n.b. This disallows use of ArrayPool, as ArrayPool-rented arrays are + // considered user-visible to threads which may still be holding on to returned instances. + + copyOfParameters = (parameters.Length <= StackAllocedArguments.MaxStackAllocArgCount) + ? MemoryMarshal.CreateSpan(ref stackArgs._arg0, parameters.Length) + : new Span(new object?[parameters.Length]); + + ParameterInfo[]? p = null; + for (int i = 0; i < parameters.Length; i++) { - p ??= GetParametersNoCopy(); - if (p[i].DefaultValue == System.DBNull.Value) - throw new ArgumentException(SR.Arg_VarMissNull, nameof(parameters)); - arg = p[i].DefaultValue!; + object? arg = parameters[i]; + RuntimeType argRT = sig.Arguments[i]; + + if (arg == Type.Missing) + { + p ??= GetParametersNoCopy(); + if (p[i].DefaultValue == System.DBNull.Value) + throw new ArgumentException(SR.Arg_VarMissNull, nameof(parameters)); + arg = p[i].DefaultValue!; + } + copyOfParameters[i] = argRT.CheckValue(arg, binder, culture, invokeAttr); } - copyOfParameters[i] = argRT.CheckValue(arg, binder, culture, invokeAttr)!; } return copyOfParameters; } + + // Helper struct to avoid intermediate object[] allocation in calls to the native reflection stack. + // Typical usage is to define a local of type default(StackAllocedArguments), then pass 'ref theLocal' + // as the first parameter to CheckArguments. CheckArguments will try to utilize storage within this + // struct instance if there's sufficient space; otherwise CheckArguments will allocate a temp array. + private protected struct StackAllocedArguments + { + internal const int MaxStackAllocArgCount = 4; + internal object? _arg0; +#pragma warning disable CA1823, CS0169, IDE0051 // accessed via 'CheckArguments' ref arithmetic + private object? _arg1; + private object? _arg2; + private object? _arg3; +#pragma warning restore CA1823, CS0169, IDE0051 + } #endregion } } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.cs index 8a02eea275ab41..c991b642cfc5ec 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.cs @@ -7,6 +7,7 @@ using System.Globalization; using System.Runtime.CompilerServices; using System.Text; +using System.Threading; using RuntimeTypeCache = System.RuntimeType.RuntimeTypeCache; namespace System.Reflection @@ -26,14 +27,16 @@ internal sealed class RuntimeConstructorInfo : ConstructorInfo, IRuntimeMethodIn private IntPtr m_handle; private MethodAttributes m_methodAttributes; private BindingFlags m_bindingFlags; - private volatile Signature? m_signature; + private Signature? m_signature; private INVOCATION_FLAGS m_invocationFlags; internal INVOCATION_FLAGS InvocationFlags { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get { - if ((m_invocationFlags & INVOCATION_FLAGS.INVOCATION_FLAGS_INITIALIZED) == 0) + [MethodImpl(MethodImplOptions.NoInlining)] // move lazy invocation flags population out of the hot path + INVOCATION_FLAGS LazyCreateInvocationFlags() { INVOCATION_FLAGS invocationFlags = INVOCATION_FLAGS.INVOCATION_FLAGS_IS_CTOR; // this is a given @@ -68,10 +71,17 @@ internal INVOCATION_FLAGS InvocationFlags invocationFlags |= INVOCATION_FLAGS.INVOCATION_FLAGS_IS_DELEGATE_CTOR; } - m_invocationFlags = invocationFlags | INVOCATION_FLAGS.INVOCATION_FLAGS_INITIALIZED; + invocationFlags |= INVOCATION_FLAGS.INVOCATION_FLAGS_INITIALIZED; + m_invocationFlags = invocationFlags; // accesses are guaranteed atomic + return invocationFlags; } - return m_invocationFlags; + INVOCATION_FLAGS flags = m_invocationFlags; + if ((flags & INVOCATION_FLAGS.INVOCATION_FLAGS_INITIALIZED) == 0) + { + flags = LazyCreateInvocationFlags(); + } + return flags; } } #endregion @@ -95,7 +105,22 @@ internal RuntimeConstructorInfo( internal override bool CacheEquals(object? o) => o is RuntimeConstructorInfo m && m.m_handle == m_handle; - private Signature Signature => m_signature ??= new Signature(this, m_declaringType); + private Signature Signature + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + [MethodImpl(MethodImplOptions.NoInlining)] // move lazy sig generation out of the hot path + Signature LazyCreateSignature() + { + Signature newSig = new Signature(this, m_declaringType); + Volatile.Write(ref m_signature, newSig); + return newSig; + } + + return m_signature ?? LazyCreateSignature(); + } + } private RuntimeType ReflectedTypeInternal => m_reflectedTypeCache.GetRuntimeType(); @@ -313,16 +338,17 @@ internal void ThrowNoInvokeException() // if we are here we passed all the previous checks. Time to look at the arguments bool wrapExceptions = (invokeAttr & BindingFlags.DoNotWrapExceptions) == 0; - if (actualCount > 0) - { - object[] arguments = CheckArguments(parameters!, binder, invokeAttr, culture, sig); - object retValue = RuntimeMethodHandle.InvokeMethod(obj, arguments, sig, false, wrapExceptions); - // copy out. This should be made only if ByRef are present. - for (int index = 0; index < arguments.Length; index++) - parameters![index] = arguments[index]; - return retValue; - } - return RuntimeMethodHandle.InvokeMethod(obj, null, sig, false, wrapExceptions); + + StackAllocedArguments stackArgs = default; + Span arguments = CheckArguments(ref stackArgs, parameters, binder, invokeAttr, culture, sig); + object? retValue = RuntimeMethodHandle.InvokeMethod(obj, arguments, sig, false, wrapExceptions); + + // copy out. This should be made only if ByRef are present. + // n.b. cannot use Span.CopyTo, as parameters.GetType() might not actually be typeof(object[]) + for (int index = 0; index < arguments.Length; index++) + parameters![index] = arguments[index]; + + return retValue; } [RequiresUnreferencedCode("Trimming may change method bodies. For example it can change some instructions, remove branches or local variables.")] @@ -366,16 +392,17 @@ public override object Invoke(BindingFlags invokeAttr, Binder? binder, object?[] // if we are here we passed all the previous checks. Time to look at the arguments bool wrapExceptions = (invokeAttr & BindingFlags.DoNotWrapExceptions) == 0; - if (actualCount > 0) - { - object[] arguments = CheckArguments(parameters!, binder, invokeAttr, culture, sig); - object retValue = RuntimeMethodHandle.InvokeMethod(null, arguments, sig, true, wrapExceptions); - // copy out. This should be made only if ByRef are present. - for (int index = 0; index < arguments.Length; index++) - parameters![index] = arguments[index]; - return retValue; - } - return RuntimeMethodHandle.InvokeMethod(null, null, sig, true, wrapExceptions); + + StackAllocedArguments stackArgs = default; + Span arguments = CheckArguments(ref stackArgs, parameters, binder, invokeAttr, culture, sig); + object retValue = RuntimeMethodHandle.InvokeMethod(null, arguments, sig, true, wrapExceptions)!; // ctor must return non-null + + // copy out. This should be made only if ByRef are present. + // n.b. cannot use Span.CopyTo, as parameters.GetType() might not actually be typeof(object[]) + for (int index = 0; index < arguments.Length; index++) + parameters![index] = arguments[index]; + + return retValue; } #endregion } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs index e0d87e7821d8ed..9d1023bda3733c 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs @@ -5,6 +5,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.Runtime.CompilerServices; using System.Security; using System.Text; using System.Threading; @@ -30,9 +31,11 @@ internal sealed class RuntimeMethodInfo : MethodInfo, IRuntimeMethodInfo internal INVOCATION_FLAGS InvocationFlags { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get { - if ((m_invocationFlags & INVOCATION_FLAGS.INVOCATION_FLAGS_INITIALIZED) == 0) + [MethodImpl(MethodImplOptions.NoInlining)] // move lazy invocation flags population out of the hot path + INVOCATION_FLAGS LazyCreateInvocationFlags() { INVOCATION_FLAGS invocationFlags = INVOCATION_FLAGS.INVOCATION_FLAGS_UNKNOWN; @@ -55,10 +58,17 @@ internal INVOCATION_FLAGS InvocationFlags invocationFlags |= INVOCATION_FLAGS.INVOCATION_FLAGS_CONTAINS_STACK_POINTERS; } - m_invocationFlags = invocationFlags | INVOCATION_FLAGS.INVOCATION_FLAGS_INITIALIZED; + invocationFlags |= INVOCATION_FLAGS.INVOCATION_FLAGS_INITIALIZED; + m_invocationFlags = invocationFlags; // accesses are guaranteed atomic + return invocationFlags; } - return m_invocationFlags; + INVOCATION_FLAGS flags = m_invocationFlags; + if ((flags & INVOCATION_FLAGS.INVOCATION_FLAGS_INITIALIZED) == 0) + { + flags = LazyCreateInvocationFlags(); + } + return flags; } } @@ -105,7 +115,22 @@ private ParameterInfo FetchReturnParameter() => internal override bool CacheEquals(object? o) => o is RuntimeMethodInfo m && m.m_handle == m_handle; - internal Signature Signature => m_signature ??= new Signature(this, m_declaringType); + internal Signature Signature + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + [MethodImpl(MethodImplOptions.NoInlining)] // move lazy sig generation out of the hot path + Signature LazyCreateSignature() + { + Signature newSig = new Signature(this, m_declaringType); + Volatile.Write(ref m_signature, newSig); + return newSig; + } + + return m_signature ?? LazyCreateSignature(); + } + } internal BindingFlags BindingFlags => m_bindingFlags; @@ -330,10 +355,11 @@ public override MethodImplAttributes GetMethodImplementationFlags() #endregion #region Invocation Logic(On MemberBase) + [MethodImpl(MethodImplOptions.AggressiveInlining)] private void CheckConsistency(object? target) { // only test instance methods - if ((m_methodAttributes & MethodAttributes.Static) != MethodAttributes.Static) + if ((m_methodAttributes & MethodAttributes.Static) == 0) { if (!m_declaringType.IsInstanceOfType(target)) { @@ -384,26 +410,23 @@ private void ThrowNoInvokeException() [Diagnostics.DebuggerHidden] public override object? Invoke(object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? parameters, CultureInfo? culture) { - object[]? arguments = InvokeArgumentsCheck(obj, invokeAttr, binder, parameters, culture); + StackAllocedArguments stackArgs = default; // try to avoid intermediate array allocation if possible + Span arguments = InvokeArgumentsCheck(ref stackArgs, obj, invokeAttr, binder, parameters, culture); bool wrapExceptions = (invokeAttr & BindingFlags.DoNotWrapExceptions) == 0; - if (arguments == null || arguments.Length == 0) - return RuntimeMethodHandle.InvokeMethod(obj, null, Signature, false, wrapExceptions); - else - { - object retValue = RuntimeMethodHandle.InvokeMethod(obj, arguments, Signature, false, wrapExceptions); + object? retValue = RuntimeMethodHandle.InvokeMethod(obj, arguments, Signature, false, wrapExceptions); - // copy out. This should be made only if ByRef are present. - for (int index = 0; index < arguments.Length; index++) - parameters![index] = arguments[index]; + // copy out. This should be made only if ByRef are present. + // n.b. cannot use Span.CopyTo, as parameters.GetType() might not actually be typeof(object[]) + for (int index = 0; index < arguments.Length; index++) + parameters![index] = arguments[index]; - return retValue; - } + return retValue; } [DebuggerStepThroughAttribute] [Diagnostics.DebuggerHidden] - private object[]? InvokeArgumentsCheck(object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? parameters, CultureInfo? culture) + private Span InvokeArgumentsCheck(ref StackAllocedArguments stackArgs, object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? parameters, CultureInfo? culture) { Signature sig = Signature; @@ -425,10 +448,12 @@ private void ThrowNoInvokeException() if (formalCount != actualCount) throw new TargetParameterCountException(SR.Arg_ParmCnt); + Span retVal = default; if (actualCount != 0) - return CheckArguments(parameters!, binder, invokeAttr, culture, sig); - else - return null; + { + retVal = CheckArguments(ref stackArgs, parameters!, binder, invokeAttr, culture, sig); + } + return retVal; } #endregion diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.CoreCLR.cs index 0187c5e29ba052..8cfaff78e938be 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.CoreCLR.cs @@ -26,7 +26,7 @@ public partial struct GCHandle #endif [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void InternalSet(IntPtr handle, object? value); + internal static extern void InternalSet(IntPtr handle, object? value); [MethodImpl(MethodImplOptions.InternalCall)] internal static extern object? InternalCompareExchange(IntPtr handle, object? value, object? oldValue); 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 cfa743db019e29..afacc85d34c863 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 @@ -600,37 +600,6 @@ public static TWrapper CreateWrapperOfType(T? o) [MethodImpl(MethodImplOptions.InternalCall)] public static extern bool IsTypeVisibleFromCom(Type t); - [SupportedOSPlatform("windows")] - public static unsafe int QueryInterface(IntPtr pUnk, ref Guid iid, out IntPtr ppv) - { - if (pUnk == IntPtr.Zero) - throw new ArgumentNullException(nameof(pUnk)); - - fixed (Guid* pIID = &iid) - fixed (IntPtr* p = &ppv) - { - return ((delegate* unmanaged)(*(*(void***)pUnk + 0 /* IUnknown.QueryInterface slot */)))(pUnk, pIID, p); - } - } - - [SupportedOSPlatform("windows")] - public static unsafe int AddRef(IntPtr pUnk) - { - if (pUnk == IntPtr.Zero) - throw new ArgumentNullException(nameof(pUnk)); - - return ((delegate* unmanaged)(*(*(void***)pUnk + 1 /* IUnknown.AddRef slot */)))(pUnk); - } - - [SupportedOSPlatform("windows")] - public static unsafe int Release(IntPtr pUnk) - { - if (pUnk == IntPtr.Zero) - throw new ArgumentNullException(nameof(pUnk)); - - return ((delegate* unmanaged)(*(*(void***)pUnk + 2 /* IUnknown.Release slot */)))(pUnk); - } - [SupportedOSPlatform("windows")] [MethodImpl(MethodImplOptions.InternalCall)] public static extern void GetNativeVariantForObject(object? obj, /* VARIANT * */ IntPtr pDstNativeVariant); diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs index 6e99db321a05d1..c89801cb802510 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -9,6 +9,7 @@ using System.Runtime.Loader; using System.Runtime.Serialization; using System.Threading; +using Internal.Runtime.CompilerServices; namespace System { @@ -963,7 +964,7 @@ internal static MdUtf8String GetUtf8Name(RuntimeMethodHandleInternal method) [DebuggerStepThroughAttribute] [Diagnostics.DebuggerHidden] [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern object InvokeMethod(object? target, object[]? arguments, Signature sig, bool constructor, bool wrapExceptions); + internal static extern object? InvokeMethod(object? target, in Span arguments, Signature sig, bool constructor, bool wrapExceptions); [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)] private static extern void GetMethodInstantiation(RuntimeMethodHandleInternal method, ObjectHandleOnStack types, Interop.BOOL fAsRuntimeTypeArray); diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs index a1d174667db10f..78bb08345933f7 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs @@ -2418,6 +2418,18 @@ private RuntimeTypeCache InitializeCache() return cache; } + internal void ClearCache() + { + // If there isn't a GCHandle yet, there's nothing more to do. + if (Volatile.Read(ref m_cache) == IntPtr.Zero) + { + return; + } + + // Set the GCHandle to null. The cache will be re-created next time it is needed. + GCHandle.InternalSet(m_cache, null); + } + private string? GetDefaultMemberName() { return Cache.GetDefaultMemberName(); diff --git a/src/coreclr/crosscomponents.cmake b/src/coreclr/crosscomponents.cmake index e8d51914951e33..03b076becfb6d6 100644 --- a/src/coreclr/crosscomponents.cmake +++ b/src/coreclr/crosscomponents.cmake @@ -1,6 +1,11 @@ # Add targets to the crosscomponents subcomponent build if (CLR_CMAKE_HOST_OS STREQUAL CLR_CMAKE_TARGET_OS) + install_clr (TARGETS + clrjit + DESTINATIONS . sharedFramework + COMPONENT crosscomponents + ) install_clr (TARGETS clrjit jitinterface_${ARCH_HOST_NAME} @@ -11,7 +16,7 @@ if (CLR_CMAKE_HOST_OS STREQUAL CLR_CMAKE_TARGET_OS) if(CLR_CMAKE_HOST_LINUX OR NOT FEATURE_CROSSBITNESS) install_clr (TARGETS crossgen - DESTINATIONS . + DESTINATIONS . sharedFramework COMPONENT crosscomponents ) endif() @@ -19,7 +24,7 @@ if (CLR_CMAKE_HOST_OS STREQUAL CLR_CMAKE_TARGET_OS) if (CLR_CMAKE_TARGET_UNIX) install_clr (TARGETS clrjit_unix_${ARCH_TARGET_NAME}_${ARCH_HOST_NAME} - DESTINATIONS . + DESTINATIONS . sharedFramework COMPONENT crosscomponents ) endif(CLR_CMAKE_TARGET_UNIX) @@ -29,7 +34,7 @@ if(NOT CLR_CMAKE_HOST_LINUX AND NOT CLR_CMAKE_HOST_OSX AND NOT FEATURE_CROSSBITN install_clr (TARGETS mscordaccore mscordbi - DESTINATIONS . + DESTINATIONS . sharedFramework COMPONENT crosscomponents ) endif() diff --git a/src/coreclr/dlls/mscordac/CMakeLists.txt b/src/coreclr/dlls/mscordac/CMakeLists.txt index 327560a030db20..9dd3329a230487 100644 --- a/src/coreclr/dlls/mscordac/CMakeLists.txt +++ b/src/coreclr/dlls/mscordac/CMakeLists.txt @@ -210,5 +210,8 @@ if(CLR_CMAKE_HOST_WIN32) string(REGEX MATCH "#define VER_FILEVERSION[ \t]+[0-9]+(,[0-9]+)+" FILE_VERSION_LINE "${NATIVE_VERSION_HEADER}") string(REGEX MATCHALL "[0-9]+" FILE_VERSION_COMPONENTS "${FILE_VERSION_LINE}") list(JOIN FILE_VERSION_COMPONENTS "." FILE_VERSION) - install(FILES $ RENAME mscordaccore_${LONG_NAME_HOST_ARCH}_${LONG_NAME_TARGET_ARCH}_${FILE_VERSION}.dll DESTINATION sharedFramework COMPONENT runtime) + install(PROGRAMS $ RENAME mscordaccore_${LONG_NAME_HOST_ARCH}_${LONG_NAME_TARGET_ARCH}_${FILE_VERSION}.dll DESTINATION sharedFramework COMPONENT runtime) + if (NOT FEATURE_CROSSBITNESS) + install(PROGRAMS $ RENAME mscordaccore_${LONG_NAME_HOST_ARCH}_${LONG_NAME_TARGET_ARCH}_${FILE_VERSION}.dll DESTINATION sharedFramework COMPONENT crosscomponents) + endif() endif() diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 8df0a785859924..4437db322e39ea 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -4964,6 +4964,16 @@ BOOL gc_heap::reserve_initial_memory (size_t normal_size, size_t large_size, siz } } + if (reserve_success && separated_poh_p) + { + for (int heap_no = 0; (reserve_success && (heap_no < num_heaps)); heap_no++) + { + if (!GCToOSInterface::VirtualCommit(memory_details.initial_pinned_heap[heap_no].memory_base, pinned_size)) + { + reserve_success = FALSE; + } + } + } return reserve_success; } @@ -6581,7 +6591,6 @@ bool gc_heap::virtual_decommit (void* address, size_t size, gc_oh_num oh, int h_ void gc_heap::virtual_free (void* add, size_t allocated_size, heap_segment* sg) { - assert(!heap_hard_limit); bool release_succeeded_p = GCToOSInterface::VirtualRelease (add, allocated_size); if (release_succeeded_p) { @@ -11739,6 +11748,11 @@ HRESULT gc_heap::initialize_gc (size_t soh_segment_size, if (!reserve_initial_memory (soh_segment_size, loh_segment_size, poh_segment_size, number_of_heaps, use_large_pages_p, separated_poh_p, heap_no_to_numa_node)) return E_OUTOFMEMORY; + if (separated_poh_p) + { + heap_hard_limit_oh[poh] = min_segment_size_hard_limit * number_of_heaps; + heap_hard_limit += heap_hard_limit_oh[poh]; + } #endif //USE_REGIONS #ifdef CARD_BUNDLE @@ -33640,7 +33654,7 @@ bool gc_heap::find_next_chunk(card_marking_enumerator& card_mark_enumerator, hea dprintf (3, ("No more chunks on heap %d\n", heap_number)); return false; } - card = card_of (chunk_low); + card = max(card, card_of(chunk_low)); card_word_end = (card_of(align_on_card_word(chunk_high)) / card_word_width); dprintf (3, ("Moved to next chunk on heap %d: [%Ix,%Ix[", heap_number, (size_t)chunk_low, (size_t)chunk_high)); } @@ -40125,6 +40139,10 @@ HRESULT GCHeap::Initialize() { return E_INVALIDARG; } + if (!gc_heap::heap_hard_limit_oh[poh] && !GCConfig::GetGCLargePages()) + { + return E_INVALIDARG; + } gc_heap::heap_hard_limit = gc_heap::heap_hard_limit_oh[soh] + gc_heap::heap_hard_limit_oh[loh] + gc_heap::heap_hard_limit_oh[poh]; } @@ -40344,7 +40362,12 @@ HRESULT GCHeap::Initialize() #ifdef MULTIPLE_HEAPS gc_heap::soh_segment_size /= 4; #endif //MULTIPLE_HEAPS - gc_heap::min_segment_size_shr = index_of_highest_set_bit (REGION_SIZE); + size_t gc_region_size = (size_t)GCConfig::GetGCRegionsSize(); + if (!power_of_two_p(gc_region_size) || ((gc_region_size * nhp * 19) > gc_heap::regions_range)) + { + return E_INVALIDARG; + } + gc_heap::min_segment_size_shr = index_of_highest_set_bit (gc_region_size); #else gc_heap::min_segment_size_shr = index_of_highest_set_bit (gc_heap::min_segment_size); #endif //USE_REGIONS diff --git a/src/coreclr/gc/gcconfig.h b/src/coreclr/gc/gcconfig.h index e7b5e1f21804de..0ab210bf48c3e0 100644 --- a/src/coreclr/gc/gcconfig.h +++ b/src/coreclr/gc/gcconfig.h @@ -102,7 +102,8 @@ class GCConfigStringHolder INT_CONFIG (GCHeapHardLimit, "GCHeapHardLimit", "System.GC.HeapHardLimit", 0, "Specifies a hard limit for the GC heap") \ INT_CONFIG (GCHeapHardLimitPercent, "GCHeapHardLimitPercent", "System.GC.HeapHardLimitPercent", 0, "Specifies the GC heap usage as a percentage of the total memory") \ INT_CONFIG (GCTotalPhysicalMemory, "GCTotalPhysicalMemory", NULL, 0, "Specifies what the GC should consider to be total physical memory") \ - INT_CONFIG (GCRegionsRange, "GCRegionsRange", NULL, 0, "Specifies the range for the GC heap") \ + INT_CONFIG (GCRegionsRange, "GCRegionsRange", NULL, 274877906944L, "Specifies the range for the GC heap") \ + INT_CONFIG (GCRegionsSize, "GCRegionsSize", NULL, 4194304, "Specifies the size for a basic GC region") \ STRING_CONFIG(LogFile, "GCLogFile", NULL, "Specifies the name of the GC log file") \ STRING_CONFIG(ConfigLogFile, "GCConfigLogFile", NULL, "Specifies the name of the GC config log file") \ INT_CONFIG (BGCFLTuningEnabled, "BGCFLTuningEnabled", NULL, 0, "Enables FL tuning") \ diff --git a/src/coreclr/gc/gcpriv.h b/src/coreclr/gc/gcpriv.h index f8455ac323cd0c..1cfc2f229ac089 100644 --- a/src/coreclr/gc/gcpriv.h +++ b/src/coreclr/gc/gcpriv.h @@ -5278,8 +5278,6 @@ class heap_segment // and free_large_regions. These decommitted regions will be returned to region_allocator which // mark the space as free blocks. // -// Make configs available to change these. -#define REGION_SIZE ((size_t)4 * 1024 * 1024) #define LARGE_REGION_FACTOR (8) #define region_alloc_free_bit (1 << (sizeof (uint32_t) * 8 - 1)) diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index cda748635c721e..5e6a2ef98993e2 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -16,7 +16,7 @@ // Note that this file is parsed by some tools, namely superpmi.py, so make sure the first line is exactly // of the form: // -// constexpr GUID JITEEVersionIdentifier = { /* a7bb194e-4e7c-4850-af12-ea9f30ea5a13 */ +// constexpr GUID JITEEVersionIdentifier = { /* 1776ab48-edfa-49be-a11f-ec216b28174c */ // // (without the leading slashes or spaces). // @@ -43,11 +43,11 @@ typedef const GUID *LPCGUID; #define GUID_DEFINED #endif // !GUID_DEFINED -constexpr GUID JITEEVersionIdentifier = { /* a1f5e9a1-ee44-42f9-9319-e2a2dbf8c5c9 */ - 0xa1f5e9a1, - 0xee44, - 0x42f9, - {0x93, 0x19, 0xe2, 0xa2, 0xdb, 0xf8, 0xc5, 0xc9} +constexpr GUID JITEEVersionIdentifier = { /* 1776ab48-edfa-49be-a11f-ec216b28174c */ + 0x1776ab48, + 0xedfa, + 0x49be, + {0xa1, 0x1f, 0xec, 0x21, 0x6b, 0x28, 0x17, 0x4c} }; ////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index 7dd138082fc564..56366cfd6d7707 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -541,12 +541,14 @@ void Compiler::optAssertionInit(bool isLocalProp) if (!isLocalProp) { - optValueNumToAsserts = new (getAllocator()) ValueNumToAssertsMap(getAllocator()); + optValueNumToAsserts = + new (getAllocator(CMK_AssertionProp)) ValueNumToAssertsMap(getAllocator(CMK_AssertionProp)); } if (optAssertionDep == nullptr) { - optAssertionDep = new (this, CMK_AssertionProp) JitExpandArray(getAllocator(), max(1, lvaCount)); + optAssertionDep = + new (this, CMK_AssertionProp) JitExpandArray(getAllocator(CMK_AssertionProp), max(1, lvaCount)); } optAssertionTraitsInit(optMaxAssertionCount); @@ -2666,12 +2668,10 @@ GenTree* Compiler::optConstantAssertionProp(AssertionDsc* curAssertion, { const unsigned lclNum = tree->GetLclNum(); -#if FEATURE_ANYCSE if (lclNumIsCSE(lclNum)) { return nullptr; } -#endif GenTree* newTree = tree; @@ -5167,13 +5167,11 @@ Compiler::fgWalkResult Compiler::optVNConstantPropCurStmt(BasicBlock* block, Sta { return WALK_CONTINUE; } -#if FEATURE_ANYCSE // Let's not conflict with CSE (to save the movw/movt). if (lclNumIsCSE(tree->AsLclVarCommon()->GetLclNum())) { return WALK_CONTINUE; } -#endif break; default: diff --git a/src/coreclr/jit/block.h b/src/coreclr/jit/block.h index aa15fa4f311df0..e411d6b32f9da1 100644 --- a/src/coreclr/jit/block.h +++ b/src/coreclr/jit/block.h @@ -537,7 +537,7 @@ struct BasicBlock : private LIR::Range weight_t bbWeight; // The dynamic execution weight of this block // getCalledCount -- get the value used to normalize weights for this method - weight_t getCalledCount(Compiler* comp); + static weight_t getCalledCount(Compiler* comp); // getBBWeight -- get the normalized weight of this block weight_t getBBWeight(Compiler* comp); @@ -647,9 +647,9 @@ struct BasicBlock : private LIR::Range } } - bool isMaxBBWeight() + bool isMaxBBWeight() const { - return (bbWeight == BB_MAX_WEIGHT); + return (bbWeight >= BB_MAX_WEIGHT); } // Returns "true" if the block is empty. Empty here means there are no statement diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 0219f104b47f22..19f374ffe27e73 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -1830,7 +1830,7 @@ void Compiler::compInit(ArenaAllocator* pAlloc, bRangeAllowStress = false; #endif -#if defined(DEBUG) || defined(LATE_DISASM) +#if defined(DEBUG) || defined(LATE_DISASM) || DUMP_FLOWGRAPHS // Initialize the method name and related info, as it is used early in determining whether to // apply stress modes, and which ones to apply. // Note that even allocating memory can invoke the stress mechanism, so ensure that both @@ -1852,7 +1852,7 @@ void Compiler::compInit(ArenaAllocator* pAlloc, info.compFullName = eeGetMethodFullName(methodHnd); info.compPerfScore = 0.0; -#endif // defined(DEBUG) || defined(LATE_DISASM) +#endif // defined(DEBUG) || defined(LATE_DISASM) || DUMP_FLOWGRAPHS #if defined(DEBUG) || defined(INLINE_DATA) info.compMethodHashPrivate = 0; @@ -5027,11 +5027,9 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl DoPhase(this, PHASE_OPTIMIZE_BRANCHES, &Compiler::optRedundantBranches); } -#if FEATURE_ANYCSE // Remove common sub-expressions // DoPhase(this, PHASE_OPTIMIZE_VALNUM_CSES, &Compiler::optOptimizeCSEs); -#endif // FEATURE_ANYCSE #if ASSERTION_PROP if (doAssertionProp) @@ -5997,11 +5995,7 @@ void Compiler::compCompileFinish() else { printf(" %3d |", optAssertionCount); -#if FEATURE_ANYCSE printf(" %3d |", optCSEcount); -#else - printf(" %3d |", 0); -#endif // FEATURE_ANYCSE } if (info.compPerfScore < 9999.995) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index e3afb451f69955..584e3568f7f51a 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -78,9 +78,7 @@ struct InitVarDscInfo; // defined in register_arg_convention.h class FgStack; // defined in fgbasic.cpp class Instrumentor; // defined in fgprofile.cpp class SpanningTreeVisitor; // defined in fgprofile.cpp -#if FEATURE_ANYCSE -class CSE_DataFlow; // defined in OptCSE.cpp -#endif +class CSE_DataFlow; // defined in OptCSE.cpp #ifdef DEBUG struct IndentStack; #endif @@ -2742,6 +2740,7 @@ class Compiler GenTree* gtNewIconEmbFldHndNode(CORINFO_FIELD_HANDLE fldHnd); GenTree* gtNewStringLiteralNode(InfoAccessType iat, void* pValue); + GenTreeIntCon* gtNewStringLiteralLength(GenTreeStrCon* node); GenTree* gtNewLconNode(__int64 value); @@ -5507,7 +5506,6 @@ class Compiler const char* fgProcessEscapes(const char* nameIn, escapeMapping_t* map); FILE* fgOpenFlowGraphFile(bool* wbDontClose, Phases phase, LPCWSTR type); bool fgDumpFlowGraph(Phases phase); - #endif // DUMP_FLOWGRAPHS #ifdef DEBUG @@ -5850,7 +5848,6 @@ class Compiler unsigned argIndex, CORINFO_CLASS_HANDLE copyBlkClass); - void fgFixupStructReturn(GenTree* call); GenTree* fgMorphLocalVar(GenTree* tree, bool forceRemorph); public: @@ -6546,6 +6543,15 @@ class Compiler } } + // Struct used in optInvertWhileLoop to count interesting constructs to boost the profitability score. + struct OptInvertCountTreeInfoType + { + int sharedStaticHelperCount; + int arrayLengthCount; + }; + + static fgWalkResult optInvertCountTreeInfo(GenTree** pTree, fgWalkData* data); + void optInvertWhileLoop(BasicBlock* block); bool optComputeLoopRep(int constInit, @@ -6574,13 +6580,8 @@ class Compiler * Optimization conditions *************************************************************************/ - bool optFastCodeOrBlendedLoop(BasicBlock::weight_t bbWeight); - bool optPentium4(void); - bool optAvoidIncDec(BasicBlock::weight_t bbWeight); bool optAvoidIntMult(void); -#if FEATURE_ANYCSE - protected: // The following is the upper limit on how many expressions we'll keep track // of for the CSE analysis. @@ -6740,9 +6741,6 @@ class Compiler return (enckey & ~TARGET_SIGN_BIT) << CSE_CONST_SHARED_LOW_BITS; } -#endif // FEATURE_ANYCSE - -#if FEATURE_VALNUM_CSE /************************************************************************** * Value Number based CSEs *************************************************************************/ @@ -6759,9 +6757,6 @@ class Compiler void optValnumCSE_Availablity(); void optValnumCSE_Heuristic(); -#endif // FEATURE_VALNUM_CSE - -#if FEATURE_ANYCSE bool optDoCSE; // True when we have found a duplicate CSE tree bool optValnumCSE_phase; // True when we are executing the optValnumCSE_phase unsigned optCSECandidateTotal; // Grand total of CSE candidates for both Lexical and ValNum @@ -6792,8 +6787,6 @@ class Compiler #endif void optOptimizeCSEs(); -#endif // FEATURE_ANYCSE - struct isVarAssgDsc { GenTree* ivaSkip; @@ -9456,12 +9449,12 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX BOOL hasCircularClassConstraints; BOOL hasCircularMethodConstraints; -#if defined(DEBUG) || defined(LATE_DISASM) +#if defined(DEBUG) || defined(LATE_DISASM) || DUMP_FLOWGRAPHS const char* compMethodName; const char* compClassName; const char* compFullName; double compPerfScore; -#endif // defined(DEBUG) || defined(LATE_DISASM) +#endif // defined(DEBUG) || defined(LATE_DISASM) || DUMP_FLOWGRAPHS #if defined(DEBUG) || defined(INLINE_DATA) // Method hash is logcally const, but computed @@ -9623,11 +9616,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #endif // TARGET_AMD64 } - bool compDoOldStructRetyping() - { - return JitConfig.JitDoOldStructRetyping(); - } - // Returns true if the method returns a value in more than one return register // TODO-ARM-Bug: Deal with multi-register genReturnLocaled structs? // TODO-ARM64: Does this apply for ARM64 too? diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index 9afb2b8c5baa3d..50a39a4d071f73 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -902,9 +902,7 @@ inline GenTree::GenTree(genTreeOps oper, var_types type DEBUGARG(bool largeNode) #ifdef DEBUG gtDebugFlags = 0; #endif // DEBUG -#if FEATURE_ANYCSE gtCSEnum = NO_CSE; -#endif // FEATURE_ANYCSE #if ASSERTION_PROP ClearAssertion(); #endif @@ -3616,27 +3614,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ -// are we compiling for fast code, or are we compiling for blended code and -// inside a loop? -// We return true for BLENDED_CODE if the Block executes more than BB_LOOP_WEIGHT_SCALE/2 -inline bool Compiler::optFastCodeOrBlendedLoop(BasicBlock::weight_t bbWeight) -{ - return (compCodeOpt() == FAST_CODE) || - ((compCodeOpt() == BLENDED_CODE) && (bbWeight > ((BB_LOOP_WEIGHT_SCALE / 2) * BB_UNITY_WEIGHT))); -} - -// are we running on a Intel Pentium 4? -inline bool Compiler::optPentium4(void) -{ - return (info.genCPU == CPU_X86_PENTIUM_4); -} - -// should we use add/sub instead of inc/dec? (faster on P4, but increases size) -inline bool Compiler::optAvoidIncDec(BasicBlock::weight_t bbWeight) -{ - return optPentium4() && optFastCodeOrBlendedLoop(bbWeight); -} - // should we try to replace integer multiplication with lea/add/shift sequences? inline bool Compiler::optAvoidIntMult(void) { @@ -4181,7 +4158,7 @@ inline void Compiler::CLR_API_Leave(API_ICorJitInfo_Names ename) bool Compiler::fgVarIsNeverZeroInitializedInProlog(unsigned varNum) { LclVarDsc* varDsc = lvaGetDesc(varNum); - bool result = varDsc->lvIsParam || lvaIsOSRLocal(varNum) || (opts.IsOSR() && (varNum == lvaGSSecurityCookie)) || + bool result = varDsc->lvIsParam || lvaIsOSRLocal(varNum) || (varNum == lvaGSSecurityCookie) || (varNum == lvaInlinedPInvokeFrameVar) || (varNum == lvaStubArgumentVar) || (varNum == lvaRetAddrVar); #if FEATURE_FIXED_OUT_ARGS diff --git a/src/coreclr/jit/compmemkind.h b/src/coreclr/jit/compmemkind.h index 1d8e0a82fe9c82..969b0c3dc2a7b5 100644 --- a/src/coreclr/jit/compmemkind.h +++ b/src/coreclr/jit/compmemkind.h @@ -47,6 +47,7 @@ CompMemKindMacro(DebugInfo) CompMemKindMacro(DebugOnly) CompMemKindMacro(Codegen) CompMemKindMacro(LoopOpt) +CompMemKindMacro(LoopClone) CompMemKindMacro(LoopHoist) CompMemKindMacro(Unknown) CompMemKindMacro(RangeCheck) diff --git a/src/coreclr/jit/compphases.h b/src/coreclr/jit/compphases.h index 49303ecbd64b9c..bb992440f8e3d4 100644 --- a/src/coreclr/jit/compphases.h +++ b/src/coreclr/jit/compphases.h @@ -72,16 +72,10 @@ CompPhaseNameMacro(PHASE_BUILD_SSA_LIVENESS, "SSA: liveness", CompPhaseNameMacro(PHASE_BUILD_SSA_DF, "SSA: DF", "SSA-DF", false, PHASE_BUILD_SSA, false) CompPhaseNameMacro(PHASE_BUILD_SSA_INSERT_PHIS, "SSA: insert phis", "SSA-PHI", false, PHASE_BUILD_SSA, false) CompPhaseNameMacro(PHASE_BUILD_SSA_RENAME, "SSA: rename", "SSA-REN", false, PHASE_BUILD_SSA, false) - CompPhaseNameMacro(PHASE_EARLY_PROP, "Early Value Propagation", "ERL-PROP", false, -1, false) CompPhaseNameMacro(PHASE_VALUE_NUMBER, "Do value numbering", "VAL-NUM", false, -1, false) - CompPhaseNameMacro(PHASE_OPTIMIZE_INDEX_CHECKS, "Optimize index checks", "OPT-CHK", false, -1, false) - -#if FEATURE_VALNUM_CSE CompPhaseNameMacro(PHASE_OPTIMIZE_VALNUM_CSES, "Optimize Valnum CSEs", "OPT-CSE", false, -1, false) -#endif - CompPhaseNameMacro(PHASE_VN_COPY_PROP, "VN based copy prop", "CP-PROP", false, -1, false) CompPhaseNameMacro(PHASE_OPTIMIZE_BRANCHES, "Redundant branch opts", "OPT-BR", false, -1, false) #if ASSERTION_PROP diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index 74b62dd4b208a8..6c61e7065fbc8e 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -133,7 +133,7 @@ unsigned emitter::emitTotalIGptrs; unsigned emitter::emitTotalIGicnt; size_t emitter::emitTotalIGsize; unsigned emitter::emitTotalIGmcnt; -unsigned emitter::emitTotalIGextend; +unsigned emitter::emitTotalIGExtend; unsigned emitter::emitTotalIDescSmallCnt; unsigned emitter::emitTotalIDescCnt; @@ -170,105 +170,105 @@ void emitterStaticStats(FILE* fout) fprintf(fout, "\n"); fprintf(fout, "insGroup:\n"); - fprintf(fout, "Offset / size of igNext = %2u / %2u\n", offsetof(insGroup, igNext), + fprintf(fout, "Offset / size of igNext = %2zu / %2zu\n", offsetof(insGroup, igNext), sizeof(igDummy->igNext)); #ifdef DEBUG - fprintf(fout, "Offset / size of igSelf = %2u / %2u\n", offsetof(insGroup, igSelf), + fprintf(fout, "Offset / size of igSelf = %2zu / %2zu\n", offsetof(insGroup, igSelf), sizeof(igDummy->igSelf)); #endif - fprintf(fout, "Offset / size of igNum = %2u / %2u\n", offsetof(insGroup, igNum), + fprintf(fout, "Offset / size of igNum = %2zu / %2zu\n", offsetof(insGroup, igNum), sizeof(igDummy->igNum)); - fprintf(fout, "Offset / size of igOffs = %2u / %2u\n", offsetof(insGroup, igOffs), + fprintf(fout, "Offset / size of igOffs = %2zu / %2zu\n", offsetof(insGroup, igOffs), sizeof(igDummy->igOffs)); - fprintf(fout, "Offset / size of igFuncIdx = %2u / %2u\n", offsetof(insGroup, igFuncIdx), + fprintf(fout, "Offset / size of igFuncIdx = %2zu / %2zu\n", offsetof(insGroup, igFuncIdx), sizeof(igDummy->igFuncIdx)); - fprintf(fout, "Offset / size of igFlags = %2u / %2u\n", offsetof(insGroup, igFlags), + fprintf(fout, "Offset / size of igFlags = %2zu / %2zu\n", offsetof(insGroup, igFlags), sizeof(igDummy->igFlags)); - fprintf(fout, "Offset / size of igSize = %2u / %2u\n", offsetof(insGroup, igSize), + fprintf(fout, "Offset / size of igSize = %2zu / %2zu\n", offsetof(insGroup, igSize), sizeof(igDummy->igSize)); - fprintf(fout, "Offset / size of igData = %2u / %2u\n", offsetof(insGroup, igData), + fprintf(fout, "Offset / size of igData = %2zu / %2zu\n", offsetof(insGroup, igData), sizeof(igDummy->igData)); - fprintf(fout, "Offset / size of igPhData = %2u / %2u\n", offsetof(insGroup, igPhData), + fprintf(fout, "Offset / size of igPhData = %2zu / %2zu\n", offsetof(insGroup, igPhData), sizeof(igDummy->igPhData)); #if EMIT_TRACK_STACK_DEPTH - fprintf(fout, "Offset / size of igStkLvl = %2u / %2u\n", offsetof(insGroup, igStkLvl), + fprintf(fout, "Offset / size of igStkLvl = %2zu / %2zu\n", offsetof(insGroup, igStkLvl), sizeof(igDummy->igStkLvl)); #endif - fprintf(fout, "Offset / size of igGCregs = %2u / %2u\n", offsetof(insGroup, igGCregs), + fprintf(fout, "Offset / size of igGCregs = %2zu / %2zu\n", offsetof(insGroup, igGCregs), sizeof(igDummy->igGCregs)); - fprintf(fout, "Offset / size of igInsCnt = %2u / %2u\n", offsetof(insGroup, igInsCnt), + fprintf(fout, "Offset / size of igInsCnt = %2zu / %2zu\n", offsetof(insGroup, igInsCnt), sizeof(igDummy->igInsCnt)); fprintf(fout, "\n"); - fprintf(fout, "Size of insGroup = %u\n", sizeof(insGroup)); + fprintf(fout, "Size of insGroup = %zu\n", sizeof(insGroup)); // insPlaceholderGroupData members fprintf(fout, "\n"); fprintf(fout, "insPlaceholderGroupData:\n"); - fprintf(fout, "Offset of igPhNext = %2u\n", offsetof(insPlaceholderGroupData, igPhNext)); - fprintf(fout, "Offset of igPhBB = %2u\n", offsetof(insPlaceholderGroupData, igPhBB)); - fprintf(fout, "Offset of igPhInitGCrefVars = %2u\n", offsetof(insPlaceholderGroupData, igPhInitGCrefVars)); - fprintf(fout, "Offset of igPhInitGCrefRegs = %2u\n", offsetof(insPlaceholderGroupData, igPhInitGCrefRegs)); - fprintf(fout, "Offset of igPhInitByrefRegs = %2u\n", offsetof(insPlaceholderGroupData, igPhInitByrefRegs)); - fprintf(fout, "Offset of igPhPrevGCrefVars = %2u\n", offsetof(insPlaceholderGroupData, igPhPrevGCrefVars)); - fprintf(fout, "Offset of igPhPrevGCrefRegs = %2u\n", offsetof(insPlaceholderGroupData, igPhPrevGCrefRegs)); - fprintf(fout, "Offset of igPhPrevByrefRegs = %2u\n", offsetof(insPlaceholderGroupData, igPhPrevByrefRegs)); - fprintf(fout, "Offset of igPhType = %2u\n", offsetof(insPlaceholderGroupData, igPhType)); - fprintf(fout, "Size of insPlaceholderGroupData = %u\n", sizeof(insPlaceholderGroupData)); + fprintf(fout, "Offset of igPhNext = %2zu\n", offsetof(insPlaceholderGroupData, igPhNext)); + fprintf(fout, "Offset of igPhBB = %2zu\n", offsetof(insPlaceholderGroupData, igPhBB)); + fprintf(fout, "Offset of igPhInitGCrefVars = %2zu\n", offsetof(insPlaceholderGroupData, igPhInitGCrefVars)); + fprintf(fout, "Offset of igPhInitGCrefRegs = %2zu\n", offsetof(insPlaceholderGroupData, igPhInitGCrefRegs)); + fprintf(fout, "Offset of igPhInitByrefRegs = %2zu\n", offsetof(insPlaceholderGroupData, igPhInitByrefRegs)); + fprintf(fout, "Offset of igPhPrevGCrefVars = %2zu\n", offsetof(insPlaceholderGroupData, igPhPrevGCrefVars)); + fprintf(fout, "Offset of igPhPrevGCrefRegs = %2zu\n", offsetof(insPlaceholderGroupData, igPhPrevGCrefRegs)); + fprintf(fout, "Offset of igPhPrevByrefRegs = %2zu\n", offsetof(insPlaceholderGroupData, igPhPrevByrefRegs)); + fprintf(fout, "Offset of igPhType = %2zu\n", offsetof(insPlaceholderGroupData, igPhType)); + fprintf(fout, "Size of insPlaceholderGroupData = %zu\n", sizeof(insPlaceholderGroupData)); fprintf(fout, "\n"); fprintf(fout, "SMALL_IDSC_SIZE = %2u\n", SMALL_IDSC_SIZE); - fprintf(fout, "Size of instrDesc = %2u\n", sizeof(emitter::instrDesc)); - // fprintf(fout, "Offset of _idIns = %2u\n", offsetof(emitter::instrDesc, _idIns )); - // fprintf(fout, "Offset of _idInsFmt = %2u\n", offsetof(emitter::instrDesc, _idInsFmt )); - // fprintf(fout, "Offset of _idOpSize = %2u\n", offsetof(emitter::instrDesc, _idOpSize )); - // fprintf(fout, "Offset of idSmallCns = %2u\n", offsetof(emitter::instrDesc, idSmallCns )); - // fprintf(fout, "Offset of _idAddrUnion= %2u\n", offsetof(emitter::instrDesc, _idAddrUnion)); + fprintf(fout, "Size of instrDesc = %2zu\n", sizeof(emitter::instrDesc)); + // fprintf(fout, "Offset of _idIns = %2zu\n", offsetof(emitter::instrDesc, _idIns )); + // fprintf(fout, "Offset of _idInsFmt = %2zu\n", offsetof(emitter::instrDesc, _idInsFmt )); + // fprintf(fout, "Offset of _idOpSize = %2zu\n", offsetof(emitter::instrDesc, _idOpSize )); + // fprintf(fout, "Offset of idSmallCns = %2zu\n", offsetof(emitter::instrDesc, idSmallCns )); + // fprintf(fout, "Offset of _idAddrUnion= %2zu\n", offsetof(emitter::instrDesc, _idAddrUnion)); // fprintf(fout, "\n"); - // fprintf(fout, "Size of _idAddrUnion= %2u\n", sizeof(((emitter::instrDesc*)0)->_idAddrUnion)); + // fprintf(fout, "Size of _idAddrUnion= %2zu\n", sizeof(((emitter::instrDesc*)0)->_idAddrUnion)); - fprintf(fout, "Size of instrDescJmp = %2u\n", sizeof(emitter::instrDescJmp)); + fprintf(fout, "Size of instrDescJmp = %2zu\n", sizeof(emitter::instrDescJmp)); #if !defined(TARGET_ARM64) - fprintf(fout, "Size of instrDescLbl = %2u\n", sizeof(emitter::instrDescLbl)); + fprintf(fout, "Size of instrDescLbl = %2zu\n", sizeof(emitter::instrDescLbl)); #endif // !defined(TARGET_ARM64) - fprintf(fout, "Size of instrDescCns = %2u\n", sizeof(emitter::instrDescCns)); - fprintf(fout, "Size of instrDescDsp = %2u\n", sizeof(emitter::instrDescDsp)); - fprintf(fout, "Size of instrDescCnsDsp = %2u\n", sizeof(emitter::instrDescCnsDsp)); + fprintf(fout, "Size of instrDescCns = %2zu\n", sizeof(emitter::instrDescCns)); + fprintf(fout, "Size of instrDescDsp = %2zu\n", sizeof(emitter::instrDescDsp)); + fprintf(fout, "Size of instrDescCnsDsp = %2zu\n", sizeof(emitter::instrDescCnsDsp)); #ifdef TARGET_XARCH - fprintf(fout, "Size of instrDescAmd = %2u\n", sizeof(emitter::instrDescAmd)); - fprintf(fout, "Size of instrDescCnsAmd = %2u\n", sizeof(emitter::instrDescCnsAmd)); + fprintf(fout, "Size of instrDescAmd = %2zu\n", sizeof(emitter::instrDescAmd)); + fprintf(fout, "Size of instrDescCnsAmd = %2zu\n", sizeof(emitter::instrDescCnsAmd)); #endif // TARGET_XARCH - fprintf(fout, "Size of instrDescCGCA = %2u\n", sizeof(emitter::instrDescCGCA)); + fprintf(fout, "Size of instrDescCGCA = %2zu\n", sizeof(emitter::instrDescCGCA)); #ifdef TARGET_ARM - fprintf(fout, "Size of instrDescReloc = %2u\n", sizeof(emitter::instrDescReloc)); + fprintf(fout, "Size of instrDescReloc = %2zu\n", sizeof(emitter::instrDescReloc)); #endif // TARGET_ARM fprintf(fout, "\n"); - fprintf(fout, "SC_IG_BUFFER_SIZE = %2u\n", SC_IG_BUFFER_SIZE); - fprintf(fout, "SMALL_IDSC_SIZE per IG buffer = %2u\n", SC_IG_BUFFER_SIZE / SMALL_IDSC_SIZE); - fprintf(fout, "instrDesc per IG buffer = %2u\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDesc)); - fprintf(fout, "instrDescJmp per IG buffer = %2u\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDescJmp)); + fprintf(fout, "SC_IG_BUFFER_SIZE = %2zu\n", SC_IG_BUFFER_SIZE); + fprintf(fout, "SMALL_IDSC_SIZE per IG buffer = %2zu\n", SC_IG_BUFFER_SIZE / SMALL_IDSC_SIZE); + fprintf(fout, "instrDesc per IG buffer = %2zu\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDesc)); + fprintf(fout, "instrDescJmp per IG buffer = %2zu\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDescJmp)); #if !defined(TARGET_ARM64) - fprintf(fout, "instrDescLbl per IG buffer = %2u\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDescLbl)); + fprintf(fout, "instrDescLbl per IG buffer = %2zu\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDescLbl)); #endif // !defined(TARGET_ARM64) - fprintf(fout, "instrDescCns per IG buffer = %2u\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDescCns)); - fprintf(fout, "instrDescDsp per IG buffer = %2u\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDescDsp)); - fprintf(fout, "instrDescCnsDsp per IG buffer = %2u\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDescCnsDsp)); + fprintf(fout, "instrDescCns per IG buffer = %2zu\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDescCns)); + fprintf(fout, "instrDescDsp per IG buffer = %2zu\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDescDsp)); + fprintf(fout, "instrDescCnsDsp per IG buffer = %2zu\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDescCnsDsp)); #ifdef TARGET_XARCH - fprintf(fout, "instrDescAmd per IG buffer = %2u\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDescAmd)); - fprintf(fout, "instrDescCnsAmd per IG buffer = %2u\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDescCnsAmd)); + fprintf(fout, "instrDescAmd per IG buffer = %2zu\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDescAmd)); + fprintf(fout, "instrDescCnsAmd per IG buffer = %2zu\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDescCnsAmd)); #endif // TARGET_XARCH - fprintf(fout, "instrDescCGCA per IG buffer = %2u\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDescCGCA)); + fprintf(fout, "instrDescCGCA per IG buffer = %2zu\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDescCGCA)); #ifdef TARGET_ARM - fprintf(fout, "instrDescReloc per IG buffer = %2u\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDescReloc)); + fprintf(fout, "instrDescReloc per IG buffer = %2zu\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDescReloc)); #endif // TARGET_ARM fprintf(fout, "\n"); fprintf(fout, "GCInfo::regPtrDsc:\n"); - fprintf(fout, "Offset of rpdNext = %2u\n", offsetof(GCInfo::regPtrDsc, rpdNext)); - fprintf(fout, "Offset of rpdOffs = %2u\n", offsetof(GCInfo::regPtrDsc, rpdOffs)); - fprintf(fout, "Offset of = %2u\n", offsetof(GCInfo::regPtrDsc, rpdPtrArg)); - fprintf(fout, "Size of GCInfo::regPtrDsc = %2u\n", sizeof(GCInfo::regPtrDsc)); + fprintf(fout, "Offset of rpdNext = %2zu\n", offsetof(GCInfo::regPtrDsc, rpdNext)); + fprintf(fout, "Offset of rpdOffs = %2zu\n", offsetof(GCInfo::regPtrDsc, rpdOffs)); + fprintf(fout, "Offset of = %2zu\n", offsetof(GCInfo::regPtrDsc, rpdPtrArg)); + fprintf(fout, "Size of GCInfo::regPtrDsc = %2zu\n", sizeof(GCInfo::regPtrDsc)); fprintf(fout, "\n"); } @@ -324,19 +324,19 @@ void emitterStats(FILE* fout) fprintf(fout, "Total of %8u methods\n", emitter::emitTotalIGmcnt); fprintf(fout, "Total of %8u insGroup\n", emitter::emitTotalIGcnt); fprintf(fout, "Total of %8u insPlaceholderGroupData\n", emitter::emitTotalPhIGcnt); - fprintf(fout, "Total of %8u extend insGroup\n", emitter::emitTotalIGextend); + fprintf(fout, "Total of %8u extend insGroup\n", emitter::emitTotalIGExtend); fprintf(fout, "Total of %8u instructions\n", emitter::emitTotalIGicnt); fprintf(fout, "Total of %8u jumps\n", emitter::emitTotalIGjmps); fprintf(fout, "Total of %8u GC livesets\n", emitter::emitTotalIGptrs); fprintf(fout, "\n"); fprintf(fout, "Max prolog instrDesc count: %8u\n", emitter::emitMaxPrologInsCnt); - fprintf(fout, "Max prolog insGroup size : %8u\n", emitter::emitMaxPrologIGSize); + fprintf(fout, "Max prolog insGroup size : %8zu\n", emitter::emitMaxPrologIGSize); fprintf(fout, "\n"); fprintf(fout, "Average of %8.1lf insGroup per method\n", (double)emitter::emitTotalIGcnt / emitter::emitTotalIGmcnt); fprintf(fout, "Average of %8.1lf insPhGroup per method\n", (double)emitter::emitTotalPhIGcnt / emitter::emitTotalIGmcnt); - fprintf(fout, "Average of %8.1lf extend IG per method\n", + fprintf(fout, "Average of %8.1lf extend IG per method\n", (double)emitter::emitTotalIGExtend / emitter::emitTotalIGmcnt); fprintf(fout, "Average of %8.1lf instructions per method\n", (double)emitter::emitTotalIGicnt / emitter::emitTotalIGmcnt); @@ -357,7 +357,7 @@ void emitterStats(FILE* fout) fprintf(fout, "Average of %8.1lf bytes per instrDesc\n", (double)emitter::emitTotalIGsize / emitter::emitTotalIGicnt); fprintf(fout, "\n"); - fprintf(fout, "A total of %8u desc. bytes\n", emitter::emitTotalIGsize); + fprintf(fout, "A total of %8zu desc. bytes\n", emitter::emitTotalIGsize); fprintf(fout, "\n"); fprintf(fout, "Total instructions: %8u\n", emitter::emitTotalInsCnt); @@ -389,7 +389,7 @@ void emitterStats(FILE* fout) fprintf(fout, "Total instrDescReloc: %8u (%5.2f%%)\n", emitter::emitTotalIDescRelocCnt, 100.0 * emitter::emitTotalIDescRelocCnt / emitter::emitTotalInsCnt); #endif // TARGET_ARM - fprintf(fout, "Total emitTotalDescAlignCnt: %8u (%5.2f%%)\n", emitter::emitTotalDescAlignCnt, + fprintf(fout, "Total instrDescAlign: %8u (%5.2f%%)\n", emitter::emitTotalDescAlignCnt, 100.0 * emitter::emitTotalDescAlignCnt / emitter::emitTotalInsCnt); fprintf(fout, "\n"); @@ -439,7 +439,7 @@ void emitterStats(FILE* fout) } } - fprintf(fout, "%8u bytes allocated in the emitter\n", emitter::emitTotMemAlloc); + fprintf(fout, "%8zu bytes allocated in the emitter\n", emitter::emitTotMemAlloc); } #endif // EMITTER_STATS diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index 50311c555c684f..0b442d16cb9f1e 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -1944,7 +1944,7 @@ class emitter instrDescJmp* emitAllocInstrJmp() { #if EMITTER_STATS - emitTotalDescAlignCnt++; + emitTotalIDescJmpCnt++; #endif // EMITTER_STATS return (instrDescJmp*)emitAllocAnyInstr(sizeof(instrDescJmp), EA_1BYTE); } @@ -2023,7 +2023,7 @@ class emitter instrDescAlign* emitAllocInstrAlign() { #if EMITTER_STATS - emitTotalIDescJmpCnt++; + emitTotalDescAlignCnt++; #endif // EMITTER_STATS return (instrDescAlign*)emitAllocAnyInstr(sizeof(instrDescAlign), EA_1BYTE); } diff --git a/src/coreclr/jit/emitarm.cpp b/src/coreclr/jit/emitarm.cpp index a2001cf0ffcd5a..ef122d6a3166ef 100644 --- a/src/coreclr/jit/emitarm.cpp +++ b/src/coreclr/jit/emitarm.cpp @@ -7742,7 +7742,7 @@ void emitter::emitInsLoadStoreOp(instruction ins, emitAttr attr, regNumber dataR if (addr->isContained()) { - assert(addr->OperGet() == GT_LCL_VAR_ADDR || addr->OperGet() == GT_LEA); + assert(addr->OperIs(GT_LCL_VAR_ADDR, GT_LCL_FLD_ADDR, GT_LEA)); DWORD lsl = 0; @@ -7827,7 +7827,21 @@ void emitter::emitInsLoadStoreOp(instruction ins, emitAttr attr, regNumber dataR } else // no Index { - if (emitIns_valid_imm_for_ldst_offset(offset, attr)) + if (addr->OperIs(GT_LCL_VAR_ADDR, GT_LCL_FLD_ADDR)) + { + GenTreeLclVarCommon* varNode = addr->AsLclVarCommon(); + unsigned lclNum = varNode->GetLclNum(); + unsigned offset = varNode->GetLclOffs(); + if (emitInsIsStore(ins)) + { + emitIns_S_R(ins, attr, dataReg, lclNum, offset); + } + else + { + emitIns_R_S(ins, attr, dataReg, lclNum, offset); + } + } + else if (emitIns_valid_imm_for_ldst_offset(offset, attr)) { // Then load/store dataReg from/to [memBase + offset] emitIns_R_R_I(ins, attr, dataReg, memBase->GetRegNum(), offset); @@ -7847,6 +7861,20 @@ void emitter::emitInsLoadStoreOp(instruction ins, emitAttr attr, regNumber dataR } else { +#ifdef DEBUG + if (addr->OperIs(GT_LCL_VAR_ADDR, GT_LCL_FLD_ADDR)) + { + // If the local var is a gcref or byref, the local var better be untracked, because we have + // no logic here to track local variable lifetime changes, like we do in the contained case + // above. E.g., for a `str r0,[r1]` for byref `r1` to local `V01`, we won't store the local + // `V01` and so the emitter can't update the GC lifetime for `V01` if this is a variable birth. + GenTreeLclVarCommon* varNode = addr->AsLclVarCommon(); + unsigned lclNum = varNode->GetLclNum(); + LclVarDsc* varDsc = emitComp->lvaGetDesc(lclNum); + assert(!varDsc->lvTracked); + } +#endif // DEBUG + if (offset != 0) { assert(emitIns_valid_imm_for_add(offset, INS_FLAGS_DONT_CARE)); diff --git a/src/coreclr/jit/emitarm64.cpp b/src/coreclr/jit/emitarm64.cpp index 7be5d50604992f..223d7b3cb4e0c4 100644 --- a/src/coreclr/jit/emitarm64.cpp +++ b/src/coreclr/jit/emitarm64.cpp @@ -13510,6 +13510,20 @@ void emitter::emitInsLoadStoreOp(instruction ins, emitAttr attr, regNumber dataR } else // addr is not contained, so we evaluate it into a register { +#ifdef DEBUG + if (addr->OperIs(GT_LCL_VAR_ADDR, GT_LCL_FLD_ADDR)) + { + // If the local var is a gcref or byref, the local var better be untracked, because we have + // no logic here to track local variable lifetime changes, like we do in the contained case + // above. E.g., for a `str r0,[r1]` for byref `r1` to local `V01`, we won't store the local + // `V01` and so the emitter can't update the GC lifetime for `V01` if this is a variable birth. + GenTreeLclVarCommon* varNode = addr->AsLclVarCommon(); + unsigned lclNum = varNode->GetLclNum(); + LclVarDsc* varDsc = emitComp->lvaGetDesc(lclNum); + assert(!varDsc->lvTracked); + } +#endif // DEBUG + // Then load/store dataReg from/to [addrReg] emitIns_R_R(ins, attr, dataReg, addr->GetRegNum()); } diff --git a/src/coreclr/jit/fgbasic.cpp b/src/coreclr/jit/fgbasic.cpp index 1f8bf814185eb8..024c32c22427c7 100644 --- a/src/coreclr/jit/fgbasic.cpp +++ b/src/coreclr/jit/fgbasic.cpp @@ -969,7 +969,7 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed impResolveToken(codeAddr, &resolvedToken, CORINFO_TOKENKIND_Method); eeGetCallInfo(&resolvedToken, (prefixFlags & PREFIX_CONSTRAINED) ? &constrainedResolvedToken : nullptr, - combine(CORINFO_CALLINFO_KINDONLY, + combine(combine(CORINFO_CALLINFO_KINDONLY, CORINFO_CALLINFO_ALLOWINSTPARAM), (opcode == CEE_CALLVIRT) ? CORINFO_CALLINFO_CALLVIRT : CORINFO_CALLINFO_NONE), &callInfo); @@ -2560,14 +2560,7 @@ void Compiler::fgFindBasicBlocks() { // The lifetime of this var might expand multiple BBs. So it is a long lifetime compiler temp. lvaInlineeReturnSpillTemp = lvaGrabTemp(false DEBUGARG("Inline return value spill temp")); - if (compDoOldStructRetyping()) - { - lvaTable[lvaInlineeReturnSpillTemp].lvType = info.compRetNativeType; - } - else - { - lvaTable[lvaInlineeReturnSpillTemp].lvType = info.compRetType; - } + lvaTable[lvaInlineeReturnSpillTemp].lvType = info.compRetType; // If the method returns a ref class, set the class of the spill temp // to the method's return value. We may update this later if it turns diff --git a/src/coreclr/jit/fgdiagnostic.cpp b/src/coreclr/jit/fgdiagnostic.cpp index 1ae0151a9ba27f..78648c8d3cd9aa 100644 --- a/src/coreclr/jit/fgdiagnostic.cpp +++ b/src/coreclr/jit/fgdiagnostic.cpp @@ -342,6 +342,20 @@ static void fprintfDouble(FILE* fgxFile, double value) // phase - A phase identifier to indicate which phase is associated with the dump // type - A (wide) string indicating the type of dump, "dot" or "xml" // +// Notes: +// The filename to use to write the data comes from the COMPlus_JitDumpFgFile or COMPlus_NgenDumpFgFile +// configuration. If unset, use "default". The "type" argument is used as a filename extension, +// e.g., "default.dot". +// +// There are several "special" filenames recognized: +// "profiled" -- only create graphs for methods with profile info, one file per method. +// "hot" -- only create graphs for the hot region, one file per method. +// "cold" -- only create graphs for the cold region, one file per method. +// "jit" -- only create graphs for JITing, one file per method. +// "all" -- create graphs for all regions, one file per method. +// "stdout" -- output to stdout, not a file. +// "stderr" -- output to stderr, not a file. +// // Return Value: // Opens a file to which a flowgraph can be dumped, whose name is based on the current // config vales. @@ -349,44 +363,43 @@ static void fprintfDouble(FILE* fgxFile, double value) FILE* Compiler::fgOpenFlowGraphFile(bool* wbDontClose, Phases phase, LPCWSTR type) { FILE* fgxFile; - LPCWSTR pattern = nullptr; - LPCWSTR filename = nullptr; - LPCWSTR pathname = nullptr; + LPCWSTR phasePattern = W("*"); // default (used in Release) is dump all phases + bool dumpFunction = true; // default (used in Release) is always dump + LPCWSTR filename = nullptr; + LPCWSTR pathname = nullptr; const char* escapedString; bool createDuplicateFgxFiles = true; + if (fgBBcount <= 1) + { + return nullptr; + } + #ifdef DEBUG if (opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT)) { - pattern = JitConfig.NgenDumpFg(); + dumpFunction = + JitConfig.NgenDumpFg().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args); filename = JitConfig.NgenDumpFgFile(); pathname = JitConfig.NgenDumpFgDir(); } else { - pattern = JitConfig.JitDumpFg(); + dumpFunction = + JitConfig.JitDumpFg().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args); filename = JitConfig.JitDumpFgFile(); pathname = JitConfig.JitDumpFgDir(); } -#endif // DEBUG - if (fgBBcount <= 1) - { - return nullptr; - } - - if (pattern == nullptr) - { - return nullptr; - } + phasePattern = JitConfig.JitDumpFgPhase(); +#endif // DEBUG - if (wcslen(pattern) == 0) + if (!dumpFunction) { return nullptr; } - LPCWSTR phasePattern = JitConfig.JitDumpFgPhase(); - LPCWSTR phaseName = PhaseShortNames[phase]; + LPCWSTR phaseName = PhaseShortNames[phase]; if (phasePattern == nullptr) { if (phase != PHASE_DETERMINE_FIRST_COLD_BLOCK) @@ -402,84 +415,6 @@ FILE* Compiler::fgOpenFlowGraphFile(bool* wbDontClose, Phases phase, LPCWSTR typ } } - if (*pattern != W('*')) - { - bool hasColon = (wcschr(pattern, W(':')) != nullptr); - - if (hasColon) - { - const char* className = info.compClassName; - if (*pattern == W('*')) - { - pattern++; - } - else - { - while ((*pattern != W(':')) && (*pattern != W('*'))) - { - if (*pattern != *className) - { - return nullptr; - } - - pattern++; - className++; - } - if (*pattern == W('*')) - { - pattern++; - } - else - { - if (*className != 0) - { - return nullptr; - } - } - } - if (*pattern != W(':')) - { - return nullptr; - } - - pattern++; - } - - const char* methodName = info.compMethodName; - if (*pattern == W('*')) - { - pattern++; - } - else - { - while ((*pattern != 0) && (*pattern != W('*'))) - { - if (*pattern != *methodName) - { - return nullptr; - } - - pattern++; - methodName++; - } - if (*pattern == W('*')) - { - pattern++; - } - else - { - if (*methodName != 0) - { - return nullptr; - } - } - } - if (*pattern != 0) - { - return nullptr; - } - } - if (filename == nullptr) { filename = W("default"); @@ -645,13 +580,15 @@ FILE* Compiler::fgOpenFlowGraphFile(bool* wbDontClose, Phases phase, LPCWSTR typ // The xml dumps are the historical mechanism for dumping the flowgraph. // The dot format can be viewed by: // - Graphviz (http://www.graphviz.org/) -// - The command "C:\Program Files (x86)\Graphviz2.38\bin\dot.exe" -Tsvg -oFoo.svg -Kdot Foo.dot -// will produce a Foo.svg file that can be opened with any svg-capable browser (e.g. IE). +// - The command: +// "C:\Program Files (x86)\Graphviz2.38\bin\dot.exe" -Tsvg -oFoo.svg -Kdot Foo.dot +// will produce a Foo.svg file that can be opened with any svg-capable browser. +// - https://sketchviz.com/ // - http://rise4fun.com/Agl/ // - Cut and paste the graph from your .dot file, replacing the digraph on the page, and then click the play // button. // - It will show a rotating '/' and then render the graph in the browser. -// MSAGL has also been open-sourced to https://github.com/Microsoft/automatic-graph-layout.git. +// MSAGL has also been open-sourced to https://github.com/Microsoft/automatic-graph-layout. // // Here are the config values that control it: // COMPlus_JitDumpFg A string (ala the COMPlus_JitDump string) indicating what methods to dump flowgraphs @@ -659,30 +596,33 @@ FILE* Compiler::fgOpenFlowGraphFile(bool* wbDontClose, Phases phase, LPCWSTR typ // COMPlus_JitDumpFgDir A path to a directory into which the flowgraphs will be dumped. // COMPlus_JitDumpFgFile The filename to use. The default is "default.[xml|dot]". // Note that the new graphs will be appended to this file if it already exists. +// COMPlus_NgenDumpFg Same as COMPlus_JitDumpFg, but for ngen compiles. +// COMPlus_NgenDumpFgDir Same as COMPlus_JitDumpFgDir, but for ngen compiles. +// COMPlus_NgenDumpFgFile Same as COMPlus_JitDumpFgFile, but for ngen compiles. // COMPlus_JitDumpFgPhase Phase(s) after which to dump the flowgraph. // Set to the short name of a phase to see the flowgraph after that phase. // Leave unset to dump after COLD-BLK (determine first cold block) or set to * for all // phases. -// COMPlus_JitDumpFgDot Set to non-zero to emit Dot instead of Xml Flowgraph dump. (Default is xml format.) +// COMPlus_JitDumpFgDot 0 for xml format, non-zero for dot format. (Default is dot format.) bool Compiler::fgDumpFlowGraph(Phases phase) { bool result = false; bool dontClose = false; - bool createDotFile = false; - if (JitConfig.JitDumpFgDot()) - { - createDotFile = true; - } + bool createDotFile = true; - FILE* fgxFile = fgOpenFlowGraphFile(&dontClose, phase, createDotFile ? W("dot") : W("fgx")); +#ifdef DEBUG + createDotFile = JitConfig.JitDumpFgDot() != 0; +#endif // DEBUG + FILE* fgxFile = fgOpenFlowGraphFile(&dontClose, phase, createDotFile ? W("dot") : W("fgx")); if (fgxFile == nullptr) { return false; } + bool validWeights = fgHaveValidEdgeWeights; - double weightDivisor = (double)fgCalledCount; + double weightDivisor = (double)BasicBlock::getCalledCount(this); const char* escapedString; const char* regionString = "NONE"; @@ -824,14 +764,18 @@ bool Compiler::fgDumpFlowGraph(Phases phase) } const char* rootTreeOpName = "n/a"; - if (block->lastNode() != nullptr) + if (block->IsLIR() || (block->lastStmt() != nullptr)) { - rootTreeOpName = GenTree::OpName(block->lastNode()->OperGet()); + if (block->lastNode() != nullptr) + { + rootTreeOpName = GenTree::OpName(block->lastNode()->OperGet()); + } } fprintf(fgxFile, "\n weight="); fprintfDouble(fgxFile, ((double)block->bbWeight) / weightDivisor); - fprintf(fgxFile, "\n codeEstimate=\"%d\"", fgGetCodeEstimate(block)); + // fgGetCodeEstimate() will assert if the costs have not yet been initialized. + // fprintf(fgxFile, "\n codeEstimate=\"%d\"", fgGetCodeEstimate(block)); fprintf(fgxFile, "\n startOffset=\"%d\"", block->bbCodeOffs); fprintf(fgxFile, "\n rootTreeOp=\"%s\"", rootTreeOpName); fprintf(fgxFile, "\n endOffset=\"%d\"", block->bbCodeOffsEnd); diff --git a/src/coreclr/jit/fginline.cpp b/src/coreclr/jit/fginline.cpp index bcb03bc56e5fe7..f31218eaddfa59 100644 --- a/src/coreclr/jit/fginline.cpp +++ b/src/coreclr/jit/fginline.cpp @@ -618,56 +618,8 @@ Compiler::fgWalkResult Compiler::fgUpdateInlineReturnExpressionPlaceHolder(GenTr #endif // FEATURE_MULTIREG_RET case SPK_EnclosingType: - { - // For enclosing type returns, we must return the call value to a temp since - // the return type is larger than the struct type. - if (!tree->IsCall()) - { - break; - } - - GenTreeCall* call = tree->AsCall(); - - assert(call->gtReturnType == TYP_STRUCT); - - if (call->gtReturnType != TYP_STRUCT) - { - break; - } - - JITDUMP("\nCall returns small struct via enclosing type, retyping. Before:\n"); - DISPTREE(call); - - // Create new struct typed temp for return value - const unsigned tmpNum = - comp->lvaGrabTemp(true DEBUGARG("small struct return temp for rejected inline")); - comp->lvaSetStruct(tmpNum, retClsHnd, false); - GenTree* assign = comp->gtNewTempAssign(tmpNum, call); - - // Modify assign tree and call return types to the primitive return type - call->gtReturnType = returnType; - call->gtType = returnType; - assign->gtType = returnType; - - // Modify the temp reference in the assign as a primitive reference via GT_LCL_FLD - GenTree* tempAsPrimitive = assign->AsOp()->gtOp1; - assert(tempAsPrimitive->gtOper == GT_LCL_VAR); - tempAsPrimitive->gtType = returnType; - tempAsPrimitive->ChangeOper(GT_LCL_FLD); - - // Return temp as value of call tree via comma - GenTree* tempAsStruct = comp->gtNewLclvNode(tmpNum, TYP_STRUCT); - GenTree* comma = comp->gtNewOperNode(GT_COMMA, TYP_STRUCT, assign, tempAsStruct); - parent->ReplaceOperand(pTree, comma); - - JITDUMP("\nAfter:\n"); - DISPTREE(comma); - } - break; - case SPK_PrimitiveType: - // We should have already retyped the call as a primitive type - // when we first imported the call + // No work needs to be done, the call has struct type and should keep it. break; case SPK_ByReference: diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 578967bac52856..3210718d041164 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -2317,17 +2317,10 @@ class MergedReturns if (comp->compMethodReturnsNativeScalarType()) { - if (!comp->compDoOldStructRetyping()) + returnLocalDsc.lvType = genActualType(comp->info.compRetType); + if (varTypeIsStruct(returnLocalDsc.lvType)) { - returnLocalDsc.lvType = genActualType(comp->info.compRetType); - if (varTypeIsStruct(returnLocalDsc.lvType)) - { - comp->lvaSetStruct(returnLocalNum, comp->info.compMethodInfo->args.retTypeClass, false); - } - } - else - { - returnLocalDsc.lvType = genActualType(comp->info.compRetNativeType); + comp->lvaSetStruct(returnLocalNum, comp->info.compMethodInfo->args.retTypeClass, false); } } else if (comp->compMethodReturnsRetBufAddr()) diff --git a/src/coreclr/jit/gcencode.cpp b/src/coreclr/jit/gcencode.cpp index c20f7d6bd74163..3c4007c6ad10b5 100644 --- a/src/coreclr/jit/gcencode.cpp +++ b/src/coreclr/jit/gcencode.cpp @@ -3931,13 +3931,24 @@ void GCInfo::gcInfoBlockHdrSave(GcInfoEncoder* gcInfoEncoder, unsigned methodSiz int offset = 0; + // OSR can report the root method's frame slot, if that method reported context. + // + bool isOsrAndUsingRootFrameSlot = false; if (compiler->opts.IsOSR()) { - PatchpointInfo* ppInfo = compiler->info.compPatchpointInfo; - offset = ppInfo->GenericContextArgOffset(); - assert(offset != -1); + PatchpointInfo* const ppInfo = compiler->info.compPatchpointInfo; + + if (ppInfo->HasKeptAliveThis()) + { + offset = ppInfo->GenericContextArgOffset(); + assert(offset != -1); + isOsrAndUsingRootFrameSlot = true; + } } - else + + // If not OSR, or OSR but newly reporting context, use the current frame offset. + // + if (!isOsrAndUsingRootFrameSlot) { offset = compiler->lvaToCallerSPRelativeOffset(compiler->lvaCachedGenericContextArgOffset(), compiler->isFramePointerUsed()); diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index ad09a52f0e157a..d4c1a188570b28 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -161,7 +161,7 @@ static void printIndent(IndentStack* indentStack) #endif -#if defined(DEBUG) || NODEBASH_STATS || MEASURE_NODE_SIZE || COUNT_AST_OPERS +#if defined(DEBUG) || NODEBASH_STATS || MEASURE_NODE_SIZE || COUNT_AST_OPERS || DUMP_FLOWGRAPHS static const char* opNames[] = { #define GTNODE(en, st, cm, ok) #en, @@ -6212,6 +6212,38 @@ GenTree* Compiler::gtNewStringLiteralNode(InfoAccessType iat, void* pValue) return tree; } +//------------------------------------------------------------------------ +// gtNewStringLiteralLength: create GenTreeIntCon node for the given string +// literal to store its length. +// +// Arguments: +// node - string literal node. +// +// Return Value: +// GenTreeIntCon node with string's length as a value or null. +// +GenTreeIntCon* Compiler::gtNewStringLiteralLength(GenTreeStrCon* node) +{ + int length = -1; + const char16_t* str = info.compCompHnd->getStringLiteral(node->gtScpHnd, node->gtSconCPX, &length); + if (length >= 0) + { + GenTreeIntCon* iconNode = gtNewIconNode(length); + + // str can be NULL for dynamic context + if (str != nullptr) + { + JITDUMP("String '\"%ws\".Length' is '%d'\n", str, length) + } + else + { + JITDUMP("String 'CNS_STR.Length' is '%d'\n", length) + } + return iconNode; + } + return nullptr; +} + /*****************************************************************************/ GenTree* Compiler::gtNewLconNode(__int64 value) @@ -10773,7 +10805,6 @@ void Compiler::gtGetLclVarNameInfo(unsigned lclNum, const char** ilKindOut, cons } else if (ilNum == (unsigned)ICorDebugInfo::UNKNOWN_ILNUM) { -#if FEATURE_ANYCSE if (lclNumIsTrueCSE(lclNum)) { ilKind = "cse"; @@ -10787,7 +10818,6 @@ void Compiler::gtGetLclVarNameInfo(unsigned lclNum, const char** ilKindOut, cons ilNum = lclNum - (optCSEstart + optCSEcount); } else -#endif // FEATURE_ANYCSE { if (lclNum == info.compLvFrameListRoot) { @@ -15591,7 +15621,6 @@ GenTree* Compiler::gtNewTempAssign( { // It could come from `ASG(struct, 0)` that was propagated to `RETURN struct(0)`, // and now it is merging to a struct again. - assert(!compDoOldStructRetyping()); assert(tmp == genReturnLocal); ok = true; } @@ -15644,7 +15673,6 @@ GenTree* Compiler::gtNewTempAssign( // 2. we are propagation `ASG(struct V01, 0)` to `RETURN(struct V01)`, `CNT_INT` doesn't `structHnd`; // in these cases, we can use the type of the merge return for the assignment. assert(val->OperIs(GT_IND, GT_LCL_FLD, GT_CNS_INT)); - assert(!compDoOldStructRetyping()); assert(tmp == genReturnLocal); valStructHnd = lvaGetStruct(genReturnLocal); assert(valStructHnd != NO_CLASS_HANDLE); @@ -15652,7 +15680,6 @@ GenTree* Compiler::gtNewTempAssign( if ((valStructHnd != NO_CLASS_HANDLE) && val->IsConstInitVal()) { - assert(!compDoOldStructRetyping()); asg = gtNewAssignNode(dest, val); } else if (varTypeIsStruct(varDsc) && ((valStructHnd != NO_CLASS_HANDLE) || varTypeIsSIMD(valTyp))) diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 6b3ff34518c0fe..c9ddd2cc4817f6 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -414,8 +414,6 @@ struct GenTree genTreeOps gtOperSave; // Only used to save gtOper when we destroy a node, to aid debugging. #endif -#if FEATURE_ANYCSE - #define NO_CSE (0) #define IS_CSE_INDEX(x) ((x) != 0) @@ -427,8 +425,6 @@ struct GenTree signed char gtCSEnum; // 0 or the CSE index (negated if def) // valid only for CSE expressions -#endif // FEATURE_ANYCSE - unsigned char gtLIRFlags; // Used for nodes that are in LIR. See LIR::Flags in lir.h for the various flags. #if ASSERTION_PROP @@ -1848,7 +1844,7 @@ struct GenTree //--------------------------------------------------------------------- -#if defined(DEBUG) || NODEBASH_STATS || MEASURE_NODE_SIZE || COUNT_AST_OPERS +#if defined(DEBUG) || NODEBASH_STATS || MEASURE_NODE_SIZE || COUNT_AST_OPERS || DUMP_FLOWGRAPHS static const char* OpName(genTreeOps op); #endif diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 1a1224d096d626..139d204638b9fb 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -812,11 +812,6 @@ void Compiler::impAssignTempGen(unsigned tmpNum, // type, this would not be necessary - but that requires additional JIT/EE interface // calls that may not actually be required - e.g. if we only access a field of a struct. - if (compDoOldStructRetyping()) - { - val->gtType = varType; - } - GenTree* dst = gtNewLclvNode(tmpNum, varType); asg = impAssignStruct(dst, val, structType, curLevel, pAfterStmt, ilOffset, block); } @@ -1389,12 +1384,6 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, var_types returnType = (var_types)srcCall->gtReturnType; - if (compDoOldStructRetyping()) - { - // We're not using a return buffer, so if we're retyping we'll change the type of 'src' to 'returnTYpe'. - src->gtType = genActualType(returnType); - } - // First we try to change this to "LclVar/LclFld = call" // if ((destAddr->gtOper == GT_ADDR) && (destAddr->AsOp()->gtOp1->gtOper == GT_LCL_VAR)) @@ -1414,14 +1403,6 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, lcl->gtFlags |= GTF_DONT_CSE; varDsc->lvIsMultiRegRet = true; } - else if ((lcl->gtType != src->gtType) && compDoOldStructRetyping()) - { - // We change this to a GT_LCL_FLD (from a GT_ADDR of a GT_LCL_VAR) - lcl->ChangeOper(GT_LCL_FLD); - fgLclFldAssign(lclNum); - lcl->gtType = src->gtType; - asgType = src->gtType; - } dest = lcl; @@ -1474,17 +1455,7 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, { // Case of inline method returning a struct in one or more registers. // We won't need a return buffer - if (compDoOldStructRetyping()) - { - var_types returnType = (var_types)call->gtReturnType; - asgType = returnType; - src->gtType = genActualType(returnType); - call->gtType = src->gtType; - } - else - { - asgType = src->gtType; - } + asgType = src->gtType; if ((destAddr->gtOper != GT_ADDR) || (destAddr->AsOp()->gtOp1->gtOper != GT_LCL_VAR)) { @@ -1624,10 +1595,7 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, dest = gtNewOperNode(GT_IND, asgType, destAddr); } } - else if (compDoOldStructRetyping()) - { - dest->gtType = asgType; - } + if (dest->OperIs(GT_LCL_VAR) && (src->IsMultiRegNode() || (src->OperIs(GT_RET_EXPR) && src->AsRetExpr()->gtInlineCandidate->AsCall()->HasMultiRegRetVal()))) @@ -3913,20 +3881,10 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, { // Optimize `ldstr + String::get_Length()` to CNS_INT // e.g. "Hello".Length => 5 - int length = -1; - const char16_t* str = info.compCompHnd->getStringLiteral(op1->AsStrCon()->gtScpHnd, - op1->AsStrCon()->gtSconCPX, &length); - if (length >= 0) + GenTreeIntCon* iconNode = gtNewStringLiteralLength(op1->AsStrCon()); + if (iconNode != nullptr) { - retNode = gtNewIconNode(length); - if (str != nullptr) // can be NULL for dynamic context - { - JITDUMP("Optimizing '\"%ws\".Length' to just '%d'\n", str, length); - } - else - { - JITDUMP("Optimizing 'CNS_STR.Length' to just '%d'\n", length); - } + retNode = iconNode; break; } } @@ -8504,11 +8462,6 @@ var_types Compiler::impImportCall(OPCODE opcode, CORINFO_CLASS_HANDLE actualMethodRetTypeSigClass; actualMethodRetTypeSigClass = sig->retTypeSigClass; - if (varTypeIsStruct(callRetTyp) && compDoOldStructRetyping()) - { - callRetTyp = impNormStructType(actualMethodRetTypeSigClass); - call->gtType = callRetTyp; - } #if !FEATURE_VARARG /* Check for varargs */ @@ -9485,32 +9438,7 @@ GenTree* Compiler::impFixupCallStructReturn(GenTreeCall* call, CORINFO_CLASS_HAN } else if (retRegCount == 1) { - if (!compDoOldStructRetyping()) - { - return call; - } - // See if the struct size is smaller than the return - // type size... - if (retTypeDesc->IsEnclosingType()) - { - // If we know for sure this call will remain a call, - // retype and return value via a suitable temp. - if ((!call->CanTailCall()) && (!call->IsInlineCandidate())) - { - call->gtReturnType = retTypeDesc->GetReturnRegType(0); - return impAssignSmallStructTypeToVar(call, retClsHnd); - } - else - { - call->gtReturnType = call->gtType; - } - } - else - { - // Return type is same size as struct, so we can - // simply retype the call. - call->gtReturnType = retTypeDesc->GetReturnRegType(0); - } + return call; } else { @@ -9548,68 +9476,35 @@ GenTree* Compiler::impFixupCallStructReturn(GenTreeCall* call, CORINFO_CLASS_HAN else { -#if FEATURE_MULTIREG_RET +#if !FEATURE_MULTIREG_RET + return call; +#else // FEATURE_MULTIREG_RET const ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc(); const unsigned retRegCount = retTypeDesc->GetReturnRegCount(); assert(retRegCount != 0); - if (!compDoOldStructRetyping() && retRegCount == 1) + if (retRegCount == 1) { return call; } -#else // !FEATURE_MULTIREG_RET - if (!compDoOldStructRetyping()) - { - return call; - } -#endif // !FEATURE_MULTIREG_RET - assert(returnType != TYP_UNKNOWN); - // See if the struct size is smaller than the return - // type size... - if (howToReturnStruct == SPK_EnclosingType) - { - // If we know for sure this call will remain a call, - // retype and return value via a suitable temp. - if ((!call->CanTailCall()) && (!call->IsInlineCandidate())) - { - call->gtReturnType = returnType; - return impAssignSmallStructTypeToVar(call, retClsHnd); - } - } - else - { - // Return type is same size as struct, so we can - // simply retype the call. - call->gtReturnType = returnType; - } + assert(returnType == TYP_STRUCT); + assert((howToReturnStruct == SPK_ByValueAsHfa) || (howToReturnStruct == SPK_ByValue)); - // ToDo: Refactor this common code sequence into its own method as it is used 4+ times - if ((returnType == TYP_LONG) && (compLongUsed == false)) - { - compLongUsed = true; - } - else if (((returnType == TYP_FLOAT) || (returnType == TYP_DOUBLE)) && (compFloatingPointUsed == false)) - { - compFloatingPointUsed = true; - } + assert(call->gtReturnType == returnType); -#if FEATURE_MULTIREG_RET - if (retRegCount >= 2) + assert(retRegCount >= 2); + if ((!call->CanTailCall()) && (!call->IsInlineCandidate())) { - if ((!call->CanTailCall()) && (!call->IsInlineCandidate())) - { - // Force a call returning multi-reg struct to be always of the IR form - // tmp = call - // - // No need to assign a multi-reg struct to a local var if: - // - It is a tail call or - // - The call is marked for in-lining later - return impAssignMultiRegTypeToVar(call, retClsHnd DEBUGARG(call->GetUnmanagedCallConv())); - } + // Force a call returning multi-reg struct to be always of the IR form + // tmp = call + // + // No need to assign a multi-reg struct to a local var if: + // - It is a tail call or + // - The call is marked for in-lining later + return impAssignMultiRegTypeToVar(call, retClsHnd DEBUGARG(call->GetUnmanagedCallConv())); } #endif // FEATURE_MULTIREG_RET } - #endif // not UNIX_AMD64_ABI return call; @@ -9620,6 +9515,18 @@ GenTree* Compiler::impFixupCallStructReturn(GenTreeCall* call, CORINFO_CLASS_HAN does not use a struct return buffer */ +//------------------------------------------------------------------------ +// impFixupStructReturnType: For struct return values it sets appropriate flags in MULTIREG returns case; +// in non-multiref case it handles two special helpers: `CORINFO_HELP_GETFIELDSTRUCT`, `CORINFO_HELP_UNBOX_NULLABLE`. +// +// Arguments: +// op - the return value; +// retClsHnd - the struct handle; +// unmgdCallConv - the calling convention of the function that returns this struct. +// +// Return Value: +// the result tree that does the return. +// GenTree* Compiler::impFixupStructReturnType(GenTree* op, CORINFO_CLASS_HANDLE retClsHnd, CorInfoCallConvExtension unmgdCallConv) @@ -9741,95 +9648,31 @@ GenTree* Compiler::impFixupStructReturnType(GenTree* op, #endif // FEATURE_MULTIREG_RET && TARGET_ARM64 - if (!compDoOldStructRetyping() && (!op->IsCall() || !op->AsCall()->TreatAsHasRetBufArg(this))) + if (!op->IsCall() || !op->AsCall()->TreatAsHasRetBufArg(this)) { // Don't retype `struct` as a primitive type in `ret` instruction. return op; } -REDO_RETURN_NODE: - // adjust the type away from struct to integral - // and no normalizing - if (op->gtOper == GT_LCL_VAR) - { - // It is possible that we now have a lclVar of scalar type. - // If so, don't transform it to GT_LCL_FLD. - LclVarDsc* varDsc = lvaGetDesc(op->AsLclVarCommon()); - if (genActualType(varDsc->TypeGet()) != genActualType(info.compRetNativeType)) - { - op->ChangeOper(GT_LCL_FLD); - } - } - else if (op->gtOper == GT_OBJ) - { - GenTree* op1 = op->AsObj()->Addr(); - - // We will fold away OBJ/ADDR, except for OBJ/ADDR/INDEX - // - // In the latter case the OBJ type may have a different type - // than the array element type, and we need to preserve the - // array element type for now. - // - if ((op1->gtOper == GT_ADDR) && (op1->AsOp()->gtOp1->gtOper != GT_INDEX)) - { - // Change '*(&X)' to 'X' and see if we can do better - op = op1->AsOp()->gtOp1; - goto REDO_RETURN_NODE; - } - op->ChangeOperUnchecked(GT_IND); - op->gtFlags |= GTF_IND_TGTANYWHERE; - } - else if (op->gtOper == GT_CALL) - { - if (op->AsCall()->TreatAsHasRetBufArg(this)) - { - // This must be one of those 'special' helpers that don't - // really have a return buffer, but instead use it as a way - // to keep the trees cleaner with fewer address-taken temps. - // - // Well now we have to materialize the the return buffer as - // an address-taken temp. Then we can return the temp. - // - // NOTE: this code assumes that since the call directly - // feeds the return, then the call must be returning the - // same structure/class/type. - // - unsigned tmpNum = lvaGrabTemp(true DEBUGARG("pseudo return buffer")); - - // No need to spill anything as we're about to return. - impAssignTempGen(tmpNum, op, info.compMethodInfo->args.retTypeClass, (unsigned)CHECK_SPILL_NONE); - - if (compDoOldStructRetyping()) - { - // Don't create both a GT_ADDR & GT_OBJ just to undo all of that; instead, - // jump directly to a GT_LCL_FLD. - op = gtNewLclvNode(tmpNum, info.compRetNativeType); - op->ChangeOper(GT_LCL_FLD); - } - else - { - op = gtNewLclvNode(tmpNum, info.compRetType); - JITDUMP("\nimpFixupStructReturnType: created a pseudo-return buffer for a special helper\n"); - DISPTREE(op); - return op; - } - } - else - { - // Don't change the gtType of the call just yet, it will get changed later. - return op; - } - } - else if (op->gtOper == GT_COMMA) - { - op->AsOp()->gtOp2 = impFixupStructReturnType(op->AsOp()->gtOp2, retClsHnd, unmgdCallConv); - } + // This must be one of those 'special' helpers that don't + // really have a return buffer, but instead use it as a way + // to keep the trees cleaner with fewer address-taken temps. + // + // Well now we have to materialize the the return buffer as + // an address-taken temp. Then we can return the temp. + // + // NOTE: this code assumes that since the call directly + // feeds the return, then the call must be returning the + // same structure/class/type. + // + unsigned tmpNum = lvaGrabTemp(true DEBUGARG("pseudo return buffer")); - op->gtType = info.compRetNativeType; + // No need to spill anything as we're about to return. + impAssignTempGen(tmpNum, op, info.compMethodInfo->args.retTypeClass, (unsigned)CHECK_SPILL_NONE); - JITDUMP("\nimpFixupStructReturnType: result of retyping is\n"); + op = gtNewLclvNode(tmpNum, info.compRetType); + JITDUMP("\nimpFixupStructReturnType: created a pseudo-return buffer for a special helper\n"); DISPTREE(op); - return op; } @@ -15748,14 +15591,10 @@ void Compiler::impImportBlockCode(BasicBlock* block) // The handle struct is returned in register op1->AsCall()->gtReturnType = GetRuntimeHandleUnderlyingType(); - if (!compDoOldStructRetyping()) - { - op1->AsCall()->gtRetClsHnd = classHandle; + op1->AsCall()->gtRetClsHnd = classHandle; #if FEATURE_MULTIREG_RET - op1->AsCall()->InitializeStructReturnType(this, classHandle, - op1->AsCall()->GetUnmanagedCallConv()); + op1->AsCall()->InitializeStructReturnType(this, classHandle, op1->AsCall()->GetUnmanagedCallConv()); #endif - } tiRetVal = typeInfo(TI_STRUCT, classHandle); } @@ -15797,13 +15636,10 @@ void Compiler::impImportBlockCode(BasicBlock* block) // The handle struct is returned in register and // it could be consumed both as `TYP_STRUCT` and `TYP_REF`. op1->AsCall()->gtReturnType = GetRuntimeHandleUnderlyingType(); - if (!compDoOldStructRetyping()) - { #if FEATURE_MULTIREG_RET - op1->AsCall()->InitializeStructReturnType(this, tokenType, op1->AsCall()->GetUnmanagedCallConv()); + op1->AsCall()->InitializeStructReturnType(this, tokenType, op1->AsCall()->GetUnmanagedCallConv()); #endif - op1->AsCall()->gtRetClsHnd = tokenType; - } + op1->AsCall()->gtRetClsHnd = tokenType; tiRetVal = verMakeTypeInfo(tokenType); impPushOnStack(op1, tiRetVal); @@ -15980,12 +15816,9 @@ void Compiler::impImportBlockCode(BasicBlock* block) op1 = gtNewHelperCallNode(helper, (var_types)((helper == CORINFO_HELP_UNBOX) ? TYP_BYREF : TYP_STRUCT), gtNewCallArgs(op2, op1)); - if (!compDoOldStructRetyping()) + if (op1->gtType == TYP_STRUCT) { - if (op1->gtType == TYP_STRUCT) - { - op1->AsCall()->gtRetClsHnd = resolvedToken.hClass; - } + op1->AsCall()->gtRetClsHnd = resolvedToken.hClass; } } @@ -17008,9 +16841,10 @@ bool Compiler::impReturnInstruction(int prefixFlags, OPCODE& opcode) impBashVarAddrsToI(op2); op2 = impImplicitIorI4Cast(op2, info.compRetType); op2 = impImplicitR4orR8Cast(op2, info.compRetType); + // Note that we allow TYP_I_IMPL<->TYP_BYREF transformation, but only TYP_I_IMPL<-TYP_REF. assertImp((genActualType(op2->TypeGet()) == genActualType(info.compRetType)) || - ((op2->TypeGet() == TYP_I_IMPL) && (info.compRetType == TYP_BYREF)) || - ((op2->TypeGet() == TYP_BYREF) && (info.compRetType == TYP_I_IMPL)) || + ((op2->TypeGet() == TYP_I_IMPL) && TypeIs(info.compRetType, TYP_BYREF)) || + (op2->TypeIs(TYP_BYREF, TYP_REF) && (info.compRetType == TYP_I_IMPL)) || (varTypeIsFloating(op2->gtType) && varTypeIsFloating(info.compRetType)) || (varTypeIsStruct(op2) && varTypeIsStruct(info.compRetType))); @@ -17059,9 +16893,10 @@ bool Compiler::impReturnInstruction(int prefixFlags, OPCODE& opcode) if (returnType != originalCallType) { - // Allow TYP_BYREF to be returned as TYP_I_IMPL and vice versa - if (((returnType == TYP_BYREF) && (originalCallType == TYP_I_IMPL)) || - ((returnType == TYP_I_IMPL) && (originalCallType == TYP_BYREF))) + // Allow TYP_BYREF to be returned as TYP_I_IMPL and vice versa. + // Allow TYP_REF to be returned as TYP_I_IMPL and NOT vice verse. + if ((TypeIs(returnType, TYP_BYREF, TYP_REF) && (originalCallType == TYP_I_IMPL)) || + ((returnType == TYP_I_IMPL) && TypeIs(originalCallType, TYP_BYREF))) { JITDUMP("Allowing return type mismatch: have %s, needed %s\n", varTypeName(returnType), varTypeName(originalCallType)); @@ -17139,68 +16974,11 @@ bool Compiler::impReturnInstruction(int prefixFlags, OPCODE& opcode) } } - // This is a bit of a workaround... - // If we are inlining a call that returns a struct, where the actual "native" return type is - // not a struct (for example, the struct is composed of exactly one int, and the native - // return type is thus an int), and the inlinee has multiple return blocks (thus, - // fgNeedReturnSpillTemp() == true, and is the index of a local var that is set - // to the *native* return type), and at least one of the return blocks is the result of - // a call, then we have a problem. The situation is like this (from a failed test case): - // - // inliner: - // // Note: valuetype plinq_devtests.LazyTests/LIX is a struct with only a single int - // call !!0 [mscorlib]System.Threading.LazyInitializer::EnsureInitialized(!!0&, bool&, object&, class [mscorlib]System.Func`1) - // - // inlinee: - // ... - // ldobj !!T // this gets bashed to a GT_LCL_FLD, type TYP_INT - // ret - // ... - // call !!0 System.Threading.LazyInitializer::EnsureInitializedCore(!!0&, bool&, - // object&, class System.Func`1) - // ret - // - // In the code above, when we call impFixupStructReturnType(), we will change the op2 return type - // of the inlinee return node, but we don't do that for GT_CALL nodes, which we delay until - // morphing when we call fgFixupStructReturn(). We do this, apparently, to handle nested - // inlining properly by leaving the correct type on the GT_CALL node through importing. - // - // To fix this, for this case, we temporarily change the GT_CALL node type to the - // native return type, which is what it will be set to eventually. We generate the - // assignment to the return temp, using the correct type, and then restore the GT_CALL - // node type. During morphing, the GT_CALL will get the correct, final, native return type. - - bool restoreType = false; - if (compDoOldStructRetyping()) - { - if ((op2->OperGet() == GT_CALL) && (info.compRetType == TYP_STRUCT)) - { - noway_assert(op2->TypeGet() == TYP_STRUCT); - op2->gtType = info.compRetNativeType; - restoreType = true; - } - } - impAssignTempGen(lvaInlineeReturnSpillTemp, op2, se.seTypeInfo.GetClassHandle(), (unsigned)CHECK_SPILL_ALL); - var_types lclRetType = op2->TypeGet(); - if (!compDoOldStructRetyping()) - { - LclVarDsc* varDsc = lvaGetDesc(lvaInlineeReturnSpillTemp); - lclRetType = varDsc->lvType; - } - - GenTree* tmpOp2 = gtNewLclvNode(lvaInlineeReturnSpillTemp, lclRetType); - - if (compDoOldStructRetyping()) - { - if (restoreType) - { - op2->gtType = TYP_STRUCT; // restore it to what it was - } - } + var_types lclRetType = lvaGetDesc(lvaInlineeReturnSpillTemp)->lvType; + GenTree* tmpOp2 = gtNewLclvNode(lvaInlineeReturnSpillTemp, lclRetType); op2 = tmpOp2; #ifdef DEBUG @@ -17469,16 +17247,8 @@ bool Compiler::impReturnInstruction(int prefixFlags, OPCODE& opcode) #endif op2 = impFixupStructReturnType(op2, retClsHnd, info.compCallConv); // return op2 - var_types returnType; - if (compDoOldStructRetyping()) - { - returnType = info.compRetNativeType; - } - else - { - returnType = info.compRetType; - } - op1 = gtNewOperNode(GT_RETURN, genActualType(returnType), op2); + var_types returnType = info.compRetType; + op1 = gtNewOperNode(GT_RETURN, genActualType(returnType), op2); } else { @@ -19067,7 +18837,7 @@ void Compiler::impMakeDiscretionaryInlineObservations(InlineInfo* pInlineInfo, I InlineCallsiteFrequency frequency = InlineCallsiteFrequency::UNUSED; // If this is a prejit root, or a maximally hot block... - if ((pInlineInfo == nullptr) || (pInlineInfo->iciBlock->bbWeight >= BB_MAX_WEIGHT)) + if ((pInlineInfo == nullptr) || (pInlineInfo->iciBlock->isMaxBBWeight())) { frequency = InlineCallsiteFrequency::HOT; } diff --git a/src/coreclr/jit/jit.h b/src/coreclr/jit/jit.h index 24fac11b945d0c..2e39bf46cc369a 100644 --- a/src/coreclr/jit/jit.h +++ b/src/coreclr/jit/jit.h @@ -406,11 +406,6 @@ class GlobalJitOptions /*****************************************************************************/ -#define FEATURE_VALNUM_CSE 1 // enable the Value Number CSE optimization logic - -// true if Value Number CSE is enabled -#define FEATURE_ANYCSE FEATURE_VALNUM_CSE - #define CSE_INTO_HANDLERS 0 #define LARGE_EXPSET 1 // Track 64 or 32 assertions/copies/consts/rangechecks diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index 055750272e335a..58156bdb04981d 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -9,10 +9,11 @@ #define OPT_CONFIG // Enable optimization level configuration. #endif +#if defined(DEBUG) + /// /// JIT /// -#if defined(DEBUG) CONFIG_INTEGER(AltJitLimit, W("AltJitLimit"), 0) // Max number of functions to use altjit for (decimal) CONFIG_INTEGER(AltJitSkipOnAssert, W("AltJitSkipOnAssert"), 0) // If AltJit hits an assert, fall back to the fallback // JIT. Useful in conjunction with @@ -64,9 +65,8 @@ CONFIG_INTEGER(JitAlignLoopAdaptive, CONFIG_INTEGER(JitDirectAlloc, W("JitDirectAlloc"), 0) CONFIG_INTEGER(JitDoubleAlign, W("JitDoubleAlign"), 1) -CONFIG_INTEGER(JitDumpASCII, W("JitDumpASCII"), 1) // Uses only ASCII characters in tree dumps -CONFIG_INTEGER(JitDumpFgDot, W("JitDumpFgDot"), 0) // Set to non-zero to emit Dot instead of Xml Flowgraph dump -CONFIG_INTEGER(JitDumpTerseLsra, W("JitDumpTerseLsra"), 1) // Produce terse dump output for LSRA +CONFIG_INTEGER(JitDumpASCII, W("JitDumpASCII"), 1) // Uses only ASCII characters in tree dumps +CONFIG_INTEGER(JitDumpTerseLsra, W("JitDumpTerseLsra"), 1) // Produce terse dump output for LSRA CONFIG_INTEGER(JitDumpToDebugger, W("JitDumpToDebugger"), 0) // Output JitDump output to the debugger CONFIG_INTEGER(JitDumpVerboseSsa, W("JitDumpVerboseSsa"), 0) // Produce especially verbose dump output for SSA CONFIG_INTEGER(JitDumpVerboseTrees, W("JitDumpVerboseTrees"), 0) // Enable more verbose tree dumps @@ -195,13 +195,14 @@ CONFIG_METHODSET(NgenUnwindDump, W("NgenUnwindDump")) // Dump the unwind codes f /// /// JIT /// -CONFIG_STRING(JitDumpFg, W("JitDumpFg")) // Dumps Xml/Dot Flowgraph for specified method +CONFIG_METHODSET(JitDumpFg, W("JitDumpFg")) // Dumps Xml/Dot Flowgraph for specified method CONFIG_STRING(JitDumpFgDir, W("JitDumpFgDir")) // Directory for Xml/Dot flowgraph dump(s) -CONFIG_STRING(JitDumpFgFile, W("JitDumpFgFile")) // Filename for Xml/Dot flowgraph dump(s) +CONFIG_STRING(JitDumpFgFile, W("JitDumpFgFile")) // Filename for Xml/Dot flowgraph dump(s) (default: "default") CONFIG_STRING(JitDumpFgPhase, W("JitDumpFgPhase")) // Phase-based Xml/Dot flowgraph support. Set to the short name of a // phase to see the flowgraph after that phase. Leave unset to dump // after COLD-BLK (determine first cold block) or set to * for all // phases +CONFIG_INTEGER(JitDumpFgDot, W("JitDumpFgDot"), 1) // 0 == dump XML format; non-zero == dump DOT format CONFIG_STRING(JitLateDisasmTo, W("JITLateDisasmTo")) CONFIG_STRING(JitRange, W("JitRange")) CONFIG_STRING(JitStressModeNames, W("JitStressModeNames")) // Internal Jit stress mode: stress using the given set of @@ -213,15 +214,16 @@ CONFIG_STRING(JitStressRange, W("JitStressRange")) // Internal Jit /// /// NGEN /// -CONFIG_STRING(NgenDumpFg, W("NgenDumpFg")) // Ngen Xml Flowgraph support -CONFIG_STRING(NgenDumpFgDir, W("NgenDumpFgDir")) // Ngen Xml Flowgraph support -CONFIG_STRING(NgenDumpFgFile, W("NgenDumpFgFile")) // Ngen Xml Flowgraph support +CONFIG_METHODSET(NgenDumpFg, W("NgenDumpFg")) // Ngen Xml/Dot flowgraph dump support +CONFIG_STRING(NgenDumpFgDir, W("NgenDumpFgDir")) // Ngen Xml/Dot flowgraph dump support +CONFIG_STRING(NgenDumpFgFile, W("NgenDumpFgFile")) // Ngen Xml/Dot flowgraph dump support /// /// JIT Hardware Intrinsics /// CONFIG_INTEGER(EnableIncompleteISAClass, W("EnableIncompleteISAClass"), 0) // Enable testing not-yet-implemented // intrinsic classes -#endif // defined(DEBUG) + +#endif // defined(DEBUG) #if FEATURE_LOOP_ALIGN CONFIG_INTEGER(JitAlignLoops, W("JitAlignLoops"), 1) // If set, align inner loops @@ -372,6 +374,7 @@ CONFIG_INTEGER(JitDoAssertionProp, W("JitDoAssertionProp"), 1) // Perform assert CONFIG_INTEGER(JitDoCopyProp, W("JitDoCopyProp"), 1) // Perform copy propagation on variables that appear redundant CONFIG_INTEGER(JitDoEarlyProp, W("JitDoEarlyProp"), 1) // Perform Early Value Propagation CONFIG_INTEGER(JitDoLoopHoisting, W("JitDoLoopHoisting"), 1) // Perform loop hoisting on loop invariant values +CONFIG_INTEGER(JitDoLoopInversion, W("JitDoLoopInversion"), 1) // Perform loop inversion on "for/while" loops CONFIG_INTEGER(JitDoRangeAnalysis, W("JitDoRangeAnalysis"), 1) // Perform range check analysis CONFIG_INTEGER(JitDoRedundantBranchOpts, W("JitDoRedundantBranchOpts"), 1) // Perform redundant branch optimizations CONFIG_INTEGER(JitDoSsa, W("JitDoSsa"), 1) // Perform Static Single Assignment (SSA) numbering on the variables @@ -503,9 +506,6 @@ CONFIG_INTEGER(JitSaveFpLrWithCalleeSavedRegisters, W("JitSaveFpLrWithCalleeSave #endif // defined(TARGET_ARM64) #endif // DEBUG -CONFIG_INTEGER(JitDoOldStructRetyping, W("JitDoOldStructRetyping"), 0) // Allow Jit to retype structs as primitive types - // when possible. - #undef CONFIG_INTEGER #undef CONFIG_STRING #undef CONFIG_METHODSET diff --git a/src/coreclr/jit/lclmorph.cpp b/src/coreclr/jit/lclmorph.cpp index 811cfc038060e7..ce195de08d2447 100644 --- a/src/coreclr/jit/lclmorph.cpp +++ b/src/coreclr/jit/lclmorph.cpp @@ -527,7 +527,7 @@ class LocalAddressVisitor final : public GenTreeVisitor assert(TopValue(0).Node() == node->gtGetOp1()); GenTreeUnOp* ret = node->AsUnOp(); GenTree* retVal = ret->gtGetOp1(); - if (!m_compiler->compDoOldStructRetyping() && retVal->OperIs(GT_LCL_VAR)) + if (retVal->OperIs(GT_LCL_VAR)) { // TODO-1stClassStructs: this block is a temporary workaround to keep diffs small, // having `doNotEnreg` affect block init and copy transformations that affect many methods. diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index f3c12658ee4562..5ed85c89eeaa18 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -2102,8 +2102,7 @@ bool Compiler::StructPromotionHelper::ShouldPromoteStructVar(unsigned lclNum) shouldPromote = false; } } - else if (!compiler->compDoOldStructRetyping() && (lclNum == compiler->genReturnLocal) && - (structPromotionInfo.fieldCnt > 1)) + else if ((lclNum == compiler->genReturnLocal) && (structPromotionInfo.fieldCnt > 1)) { // TODO-1stClassStructs: a temporary solution to keep diffs small, it will be fixed later. shouldPromote = false; @@ -3239,6 +3238,7 @@ unsigned Compiler::lvaLclExactSize(unsigned varNum) // if we don't have profile data then getCalledCount will return BB_UNITY_WEIGHT (100) // otherwise it returns the number of times that profile data says the method was called. // +// static BasicBlock::weight_t BasicBlock::getCalledCount(Compiler* comp) { // when we don't have profile data then fgCalledCount will be BB_UNITY_WEIGHT (100) @@ -6267,19 +6267,15 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals() } #endif // JIT32_GCENCODER - // OSR methods use the original method slot for the cached kept alive this, - // so don't need to allocate a slot on the new frame. - if (opts.IsOSR()) - { - if (lvaKeepAliveAndReportThis()) - { - PatchpointInfo* ppInfo = info.compPatchpointInfo; - assert(ppInfo->HasKeptAliveThis()); - int originalOffset = ppInfo->KeptAliveThisOffset(); - lvaCachedGenericContextArgOffs = originalFrameStkOffs + originalOffset; - } - } - else if (lvaReportParamTypeArg()) + // For OSR methods, param type args are always reportable via the root method frame slot. + // (see gcInfoBlockHdrSave) and so do not need a new slot on the frame. + // + // OSR methods may also be able to use the root frame kept alive this, if the root + // method needed to report this. + // + // Inlining done under OSR may introduce new reporting, in which case the OSR frame + // must allocate a slot. + if (!opts.IsOSR() && lvaReportParamTypeArg()) { #ifdef JIT32_GCENCODER noway_assert(codeGen->isFramePointerUsed()); @@ -6292,10 +6288,25 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals() #ifndef JIT32_GCENCODER else if (lvaKeepAliveAndReportThis()) { - // When "this" is also used as generic context arg. - lvaIncrementFrameSize(TARGET_POINTER_SIZE); - stkOffs -= TARGET_POINTER_SIZE; - lvaCachedGenericContextArgOffs = stkOffs; + bool canUseExistingSlot = false; + if (opts.IsOSR()) + { + PatchpointInfo* ppInfo = info.compPatchpointInfo; + if (ppInfo->HasKeptAliveThis()) + { + int originalOffset = ppInfo->KeptAliveThisOffset(); + lvaCachedGenericContextArgOffs = originalFrameStkOffs + originalOffset; + canUseExistingSlot = true; + } + } + + if (!canUseExistingSlot) + { + // When "this" is also used as generic context arg. + lvaIncrementFrameSize(TARGET_POINTER_SIZE); + stkOffs -= TARGET_POINTER_SIZE; + lvaCachedGenericContextArgOffs = stkOffs; + } } #endif diff --git a/src/coreclr/jit/lir.cpp b/src/coreclr/jit/lir.cpp index 08257c022799c4..beac0b069e2142 100644 --- a/src/coreclr/jit/lir.cpp +++ b/src/coreclr/jit/lir.cpp @@ -1541,7 +1541,7 @@ bool LIR::Range::CheckLIR(Compiler* compiler, bool checkUnusedValues) const slowNode = slowNode->gtNext; } - SmallHashTable unusedDefs(compiler->getAllocator()); + SmallHashTable unusedDefs(compiler->getAllocatorDebugOnly()); bool pastPhis = false; GenTree* prev = nullptr; diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 3eaaac1408bc46..1c4aac1af08cac 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -243,7 +243,6 @@ GenTree* Lowering::LowerNode(GenTree* node) case GT_STORE_OBJ: if (node->AsBlk()->Data()->IsCall()) { - assert(!comp->compDoOldStructRetyping()); LowerStoreSingleRegCallStruct(node->AsBlk()); break; } @@ -2981,33 +2980,15 @@ void Lowering::LowerRet(GenTreeUnOp* ret) bool doPrimitiveBitcast = false; if (needBitcast) { - if (comp->compDoOldStructRetyping()) - { - // `struct A { SIMD12/16 }` on `UNIX_AMD64_ABI` is an example when - // `varTypeUsesFloatReg` returns different values for `ret` and `ret->gtGetOp1()`, - // but doesn't need a primitive bitcase. - doPrimitiveBitcast = !ret->TypeIs(TYP_STRUCT); - } - else - { - doPrimitiveBitcast = (!varTypeIsStruct(ret) && !varTypeIsStruct(retVal)); - } + doPrimitiveBitcast = (!varTypeIsStruct(ret) && !varTypeIsStruct(retVal)); } if (doPrimitiveBitcast) { -// Add a simple bitcast for an old retyping or when both types are not structs. -// If one type is a struct it will be handled below for !compDoOldStructRetyping. +// Add a simple bitcast when both types are not structs. +// If one type is a struct it will be handled below. #if defined(DEBUG) - if (comp->compDoOldStructRetyping()) - { - assert(varTypeIsSIMD(ret) || !varTypeIsStruct(ret)); - assert(varTypeIsSIMD(retVal) || !varTypeIsStruct(retVal)); - } - else - { - assert(!varTypeIsStruct(ret) && !varTypeIsStruct(retVal)); - } + assert(!varTypeIsStruct(ret) && !varTypeIsStruct(retVal)); #endif GenTree* bitcast = comp->gtNewBitCastNode(ret->TypeGet(), retVal); @@ -3033,9 +3014,8 @@ void Lowering::LowerRet(GenTreeUnOp* ret) #ifdef DEBUG if (varTypeIsStruct(ret->TypeGet()) != varTypeIsStruct(retVal->TypeGet())) { - if (!comp->compDoOldStructRetyping() && varTypeIsStruct(ret->TypeGet())) + if (varTypeIsStruct(ret->TypeGet())) { - assert(!comp->compDoOldStructRetyping()); assert(comp->info.compRetNativeType != TYP_STRUCT); var_types retActualType = genActualType(comp->info.compRetNativeType); @@ -3059,22 +3039,9 @@ void Lowering::LowerRet(GenTreeUnOp* ret) } else if (!ret->TypeIs(TYP_VOID) && varTypeIsStruct(retVal)) { - if (comp->compDoOldStructRetyping()) - { -#ifdef FEATURE_SIMD - assert(ret->TypeIs(TYP_DOUBLE)); - assert(retVal->TypeIs(TYP_SIMD8)); -#else - unreached(); -#endif - } - else - { - // Return struct as a primitive using Unsafe cast. - assert(!comp->compDoOldStructRetyping()); - assert(retVal->OperIs(GT_LCL_VAR)); - LowerRetSingleRegStructLclVar(ret); - } + // Return struct as a primitive using Unsafe cast. + assert(retVal->OperIs(GT_LCL_VAR)); + LowerRetSingleRegStructLclVar(ret); } } @@ -3111,7 +3078,6 @@ void Lowering::LowerStoreLocCommon(GenTreeLclVarCommon* lclStore) assert(varDsc->CanBeReplacedWithItsField(comp) || varDsc->lvDoNotEnregister || !varDsc->lvPromoted); if (varDsc->CanBeReplacedWithItsField(comp)) { - assert(!comp->compDoOldStructRetyping()); assert(varDsc->lvFieldCnt == 1); unsigned fldNum = varDsc->lvFieldLclStart; LclVarDsc* fldDsc = comp->lvaGetDesc(fldNum); @@ -3165,7 +3131,6 @@ void Lowering::LowerStoreLocCommon(GenTreeLclVarCommon* lclStore) #if defined(TARGET_XARCH) && !defined(UNIX_AMD64_ABI) // Windows x64 doesn't have multireg returns, // x86 uses it only for long return type, not for structs. - assert(!comp->compDoOldStructRetyping()); assert(slotCount == 1); assert(regType != TYP_UNDEF); #else // !TARGET_XARCH || UNIX_AMD64_ABI @@ -3177,7 +3142,6 @@ void Lowering::LowerStoreLocCommon(GenTreeLclVarCommon* lclStore) } else { - assert(!comp->compDoOldStructRetyping()); unsigned size = layout->GetSize(); assert((size <= 8) || (size == 16)); bool isPowerOf2 = (((size - 1) & size) == 0); @@ -3252,16 +3216,7 @@ void Lowering::LowerRetStruct(GenTreeUnOp* ret) { assert(varTypeIsSIMD(ret->gtGetOp1())); assert(comp->compMethodReturnsMultiRegRegTypeAlternate()); - if (!comp->compDoOldStructRetyping()) - { - ret->ChangeType(comp->info.compRetNativeType); - } - else - { - // With old struct retyping a value that is returned as HFA - // could have both SIMD* or STRUCT types, keep it as it. - return; - } + ret->ChangeType(comp->info.compRetNativeType); } else { @@ -3270,7 +3225,6 @@ void Lowering::LowerRetStruct(GenTreeUnOp* ret) if (retVal->TypeGet() != ret->TypeGet()) { assert(retVal->OperIs(GT_LCL_VAR)); - assert(!comp->compDoOldStructRetyping()); LowerRetSingleRegStructLclVar(ret); } return; @@ -3284,7 +3238,6 @@ void Lowering::LowerRetStruct(GenTreeUnOp* ret) return; } - assert(!comp->compDoOldStructRetyping()); assert(ret->OperIs(GT_RETURN)); assert(varTypeIsStruct(ret)); @@ -3394,7 +3347,6 @@ void Lowering::LowerRetStruct(GenTreeUnOp* ret) void Lowering::LowerRetSingleRegStructLclVar(GenTreeUnOp* ret) { assert(!comp->compMethodReturnsMultiRegRegTypeAlternate()); - assert(!comp->compDoOldStructRetyping()); assert(ret->OperIs(GT_RETURN)); GenTreeLclVarCommon* lclVar = ret->gtGetOp1()->AsLclVar(); assert(lclVar->OperIs(GT_LCL_VAR)); @@ -3485,7 +3437,6 @@ void Lowering::LowerCallStruct(GenTreeCall* call) } } - assert(!comp->compDoOldStructRetyping()); CORINFO_CLASS_HANDLE retClsHnd = call->gtRetClsHnd; Compiler::structPassingKind howToReturnStruct; var_types returnType = comp->getReturnTypeForStruct(retClsHnd, call->GetUnmanagedCallConv(), &howToReturnStruct); @@ -3546,8 +3497,6 @@ void Lowering::LowerCallStruct(GenTreeCall* call) // void Lowering::LowerStoreSingleRegCallStruct(GenTreeBlk* store) { - assert(!comp->compDoOldStructRetyping()); - assert(varTypeIsStruct(store)); assert(store->Data()->IsCall()); GenTreeCall* call = store->Data()->AsCall(); assert(!call->HasMultiRegRetVal()); diff --git a/src/coreclr/jit/lowerarmarch.cpp b/src/coreclr/jit/lowerarmarch.cpp index cf5d6e34b0137a..0a9c8eb7f86f55 100644 --- a/src/coreclr/jit/lowerarmarch.cpp +++ b/src/coreclr/jit/lowerarmarch.cpp @@ -1453,13 +1453,17 @@ void Lowering::ContainCheckIndir(GenTreeIndir* indirNode) MakeSrcContained(indirNode, addr); } } + else if (addr->OperIs(GT_LCL_VAR_ADDR, GT_LCL_FLD_ADDR)) + { + // These nodes go into an addr mode: + // - GT_LCL_VAR_ADDR, GT_LCL_FLD_ADDR is a stack addr mode. + MakeSrcContained(indirNode, addr); + } #ifdef TARGET_ARM64 - else if (addr->OperIs(GT_CLS_VAR_ADDR, GT_LCL_VAR_ADDR, GT_LCL_FLD_ADDR)) + else if (addr->OperIs(GT_CLS_VAR_ADDR)) { // These nodes go into an addr mode: // - GT_CLS_VAR_ADDR turns into a constant. - // - GT_LCL_VAR_ADDR, GT_LCL_FLD_ADDR is a stack addr mode. - // make this contained, it turns into a constant that goes into an addr mode MakeSrcContained(indirNode, addr); } diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 8fbce8a586e796..7ce35872d1bc08 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -5114,7 +5114,7 @@ void Compiler::fgMakeOutgoingStructArgCopy(GenTreeCall* call, GenTree* arg = fgMakeTmpArgNode(argEntry); // Change the expression to "(tmp=val),tmp" - arg = gtNewOperNode(GT_COMMA, arg->TypeGet(), copyBlk, arg); + arg = gtNewOperNode(GT_COMMA, arg->TypeGet(), copyBlk, arg); #endif // FEATURE_FIXED_OUT_ARGS @@ -5166,84 +5166,6 @@ void Compiler::fgAddSkippedRegsInPromotedStructArg(LclVarDsc* varDsc, #endif // TARGET_ARM -//**************************************************************************** -// fgFixupStructReturn: -// The companion to impFixupCallStructReturn. Now that the importer is done -// change the gtType to the precomputed native return type -// requires that callNode currently has a struct type -// -void Compiler::fgFixupStructReturn(GenTree* callNode) -{ - if (!compDoOldStructRetyping()) - { - return; - } - assert(varTypeIsStruct(callNode)); - - GenTreeCall* call = callNode->AsCall(); - bool callHasRetBuffArg = call->HasRetBufArg(); - bool isHelperCall = call->IsHelperCall(); - - // Decide on the proper return type for this call that currently returns a struct - // - CORINFO_CLASS_HANDLE retClsHnd = call->gtRetClsHnd; - Compiler::structPassingKind howToReturnStruct; - var_types returnType; - - // There are a couple of Helper Calls that say they return a TYP_STRUCT but they - // expect this method to re-type this to a TYP_REF (what is in call->gtReturnType) - // - // CORINFO_HELP_METHODDESC_TO_STUBRUNTIMEMETHOD - // CORINFO_HELP_FIELDDESC_TO_STUBRUNTIMEFIELD - // CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE_MAYBENULL - // - if (isHelperCall) - { - assert(!callHasRetBuffArg); - assert(retClsHnd == NO_CLASS_HANDLE); - - // Now that we are past the importer, re-type this node - howToReturnStruct = SPK_PrimitiveType; - returnType = (var_types)call->gtReturnType; - } - else - { - returnType = getReturnTypeForStruct(retClsHnd, call->GetUnmanagedCallConv(), &howToReturnStruct); - } - - if (howToReturnStruct == SPK_ByReference) - { - assert(returnType == TYP_UNKNOWN); - assert(callHasRetBuffArg); - } - else - { - assert(returnType != TYP_UNKNOWN); - - if (!varTypeIsStruct(returnType)) - { - // Widen the primitive type if necessary - returnType = genActualType(returnType); - } - call->gtType = returnType; - } - -#if FEATURE_MULTIREG_RET - // Either we don't have a struct now or if struct, then it is a struct returned in regs or in return buffer. - assert((call->gtType != TYP_STRUCT) || call->HasMultiRegRetVal() || callHasRetBuffArg); -#else // !FEATURE_MULTIREG_RET - // No more struct returns - assert(call->TypeGet() != TYP_STRUCT); -#endif - -#if !defined(UNIX_AMD64_ABI) - // If it was a struct return, it has been transformed into a call - // with a return buffer (that returns TYP_VOID) or into a return - // of a primitive/enregisterable type - assert(!callHasRetBuffArg || (call->TypeGet() == TYP_VOID)); -#endif -} - /***************************************************************************** * * A little helper used to rearrange nested commutative operations. The @@ -6893,7 +6815,7 @@ bool Compiler::fgCanFastTailCall(GenTreeCall* callee, const char** failReason) #ifdef DEBUG if (callee->IsTailPrefixedCall()) { - var_types retType = (compDoOldStructRetyping() ? info.compRetNativeType : info.compRetType); + var_types retType = info.compRetType; assert(impTailCallRetTypeCompatible(retType, info.compMethodInfo->args.retTypeClass, info.compCallConv, (var_types)callee->gtReturnType, callee->gtRetClsHnd, callee->GetUnmanagedCallConv())); @@ -9115,10 +9037,6 @@ Statement* Compiler::fgAssignRecursiveCallArgToCallerParam(GenTree* arg, GenTree* Compiler::fgMorphCall(GenTreeCall* call) { - if (varTypeIsStruct(call)) - { - fgFixupStructReturn(call); - } if (call->CanTailCall()) { GenTree* newNode = fgMorphPotentialTailCall(call); @@ -10590,8 +10508,7 @@ GenTree* Compiler::fgMorphGetStructAddr(GenTree** pTree, CORINFO_CLASS_HANDLE cl { // TODO: Consider using lvaGrabTemp and gtNewTempAssign instead, since we're // not going to use "temp" - GenTree* temp = fgInsertCommaFormTemp(pTree, clsHnd); - assert(!compDoOldStructRetyping()); + GenTree* temp = fgInsertCommaFormTemp(pTree, clsHnd); unsigned lclNum = temp->gtEffectiveVal()->AsLclVar()->GetLclNum(); lvaSetVarDoNotEnregister(lclNum DEBUG_ARG(DNER_VMNeedsStackAddr)); addr = fgMorphGetStructAddr(pTree, clsHnd, isRValue); @@ -10938,7 +10855,7 @@ GenTree* Compiler::fgMorphCopyBlock(GenTree* tree) } #endif // FEATURE_MULTIREG_RET - if (src->IsCall() && !compDoOldStructRetyping()) + if (src->IsCall()) { if (dest->OperIs(GT_OBJ)) { @@ -12512,6 +12429,20 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac) #endif // !TARGET_64BIT break; + case GT_ARR_LENGTH: + if (op1->OperIs(GT_CNS_STR)) + { + // Optimize `ldstr + String::get_Length()` to CNS_INT + // e.g. "Hello".Length => 5 + GenTreeIntCon* iconNode = gtNewStringLiteralLength(op1->AsStrCon()); + if (iconNode != nullptr) + { + INDEBUG(iconNode->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); + return iconNode; + } + } + break; + case GT_DIV: // Replace "val / dcon" with "val * (1.0 / dcon)" if dcon is a power of two. // Powers of two within range are always exactly represented, @@ -12843,7 +12774,7 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac) return tree; } - if (!compDoOldStructRetyping() && !tree->TypeIs(TYP_VOID)) + if (!tree->TypeIs(TYP_VOID)) { if (op1->OperIs(GT_OBJ, GT_BLK, GT_IND)) { @@ -13614,14 +13545,12 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac) goto SKIP; } -#if FEATURE_ANYCSE /* If the LCL_VAR is a CSE temp then bail, it could have multiple defs/uses */ // Fix 383856 X86/ARM ILGEN if (lclNumIsCSE(lclNum)) { goto SKIP; } -#endif /* We also must be assigning the result of a RELOP */ if (asg->AsOp()->gtOp1->gtOper != GT_LCL_VAR) @@ -15253,7 +15182,6 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac) // GenTree* Compiler::fgMorphRetInd(GenTreeUnOp* ret) { - assert(!compDoOldStructRetyping()); assert(ret->OperIs(GT_RETURN)); assert(ret->gtGetOp1()->OperIs(GT_IND, GT_BLK, GT_OBJ)); GenTreeIndir* ind = ret->gtGetOp1()->AsIndir(); diff --git a/src/coreclr/jit/optcse.cpp b/src/coreclr/jit/optcse.cpp index 2b9bebd53d5b07..c37e7207f088e4 100644 --- a/src/coreclr/jit/optcse.cpp +++ b/src/coreclr/jit/optcse.cpp @@ -16,10 +16,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #pragma hdrstop #endif -/*****************************************************************************/ -#if FEATURE_ANYCSE -/*****************************************************************************/ - /* static */ const size_t Compiler::s_optCSEhashSizeInitial = EXPSET_SZ * 2; const size_t Compiler::s_optCSEhashGrowthFactor = 2; @@ -219,7 +215,7 @@ bool Compiler::optCSE_canSwap(GenTree* op1, GenTree* op2) // If we haven't setup cseMaskTraits, do it now if (cseMaskTraits == nullptr) { - cseMaskTraits = new (getAllocator()) BitVecTraits(optCSECandidateCount, this); + cseMaskTraits = new (getAllocator(CMK_CSE)) BitVecTraits(optCSECandidateCount, this); } optCSE_MaskData op1MaskData; @@ -338,10 +334,6 @@ bool Compiler::optCSEcostCmpSz::operator()(const CSEdsc* dsc1, const CSEdsc* dsc return dsc1->csdIndex < dsc2->csdIndex; } -/*****************************************************************************/ -#if FEATURE_VALNUM_CSE -/*****************************************************************************/ - /***************************************************************************** * * Initialize the Value Number CSE tracking logic. @@ -975,7 +967,7 @@ void Compiler::optCseUpdateCheckedBoundMap(GenTree* compare) if (optCseCheckedBoundMap == nullptr) { // Allocate map on first use. - optCseCheckedBoundMap = new (getAllocator()) NodeToNodeMap(getAllocator()); + optCseCheckedBoundMap = new (getAllocator(CMK_CSE)) NodeToNodeMap(getAllocator()); } optCseCheckedBoundMap->Set(bound, compare); @@ -1005,7 +997,7 @@ void Compiler::optValnumCSE_InitDataFlow() const unsigned bitCount = (optCSECandidateCount * 2) + 1; // Init traits and cseCallKillsMask bitvectors. - cseLivenessTraits = new (getAllocator()) BitVecTraits(bitCount, this); + cseLivenessTraits = new (getAllocator(CMK_CSE)) BitVecTraits(bitCount, this); cseCallKillsMask = BitVecOps::MakeEmpty(cseLivenessTraits); for (unsigned inx = 0; inx < optCSECandidateCount; inx++) { @@ -3471,8 +3463,6 @@ void Compiler::optOptimizeValnumCSEs() optValnumCSE_phase = false; } -#endif // FEATURE_VALNUM_CSE - /***************************************************************************** * * The following determines whether the given expression is a worthy CSE @@ -3829,11 +3819,9 @@ void Compiler::optOptimizeCSEs() optCSECandidateCount = 0; optCSEstart = lvaCount; -#if FEATURE_VALNUM_CSE INDEBUG(optEnsureClearCSEInfo()); optOptimizeValnumCSEs(); EndPhase(PHASE_OPTIMIZE_VALNUM_CSES); -#endif // FEATURE_VALNUM_CSE } /***************************************************************************** @@ -3888,7 +3876,3 @@ void Compiler::optEnsureClearCSEInfo() } #endif // DEBUG - -/*****************************************************************************/ -#endif // FEATURE_ANYCSE -/*****************************************************************************/ diff --git a/src/coreclr/jit/optimizer.cpp b/src/coreclr/jit/optimizer.cpp index 17b4a1858cea72..6545a894c4e579 100644 --- a/src/coreclr/jit/optimizer.cpp +++ b/src/coreclr/jit/optimizer.cpp @@ -38,11 +38,9 @@ void Compiler::optInit() optNativeCallCount = 0; optAssertionCount = 0; optAssertionDep = nullptr; -#if FEATURE_ANYCSE optCSECandidateTotal = 0; optCSEstart = UINT_MAX; optCSEcount = 0; -#endif // FEATURE_ANYCSE } DataFlow::DataFlow(Compiler* pCompiler) : m_pCompiler(pCompiler) @@ -228,17 +226,10 @@ void Compiler::optMarkLoopBlocks(BasicBlock* begBlk, BasicBlock* endBlk, bool ex scale = scale / 2; } - // - // Set the new weight - // curBlk->scaleBBWeight(scale); } -#ifdef DEBUG - if (verbose) - { - printf("\n " FMT_BB "(wt=%s)", curBlk->bbNum, refCntWtd2str(curBlk->getBBWeight(this))); - } -#endif + + JITDUMP("\n " FMT_BB "(wt=" FMT_WT ")", curBlk->bbNum, curBlk->getBBWeight(this)); } } @@ -347,41 +338,19 @@ void Compiler::optUnmarkLoopBlocks(BasicBlock* begBlk, BasicBlock* endBlk) // if (!curBlk->isMaxBBWeight() && !curBlk->hasProfileWeight()) { - BasicBlock::weight_t weight = curBlk->bbWeight; + BasicBlock::weight_t scale = 1.0f / BB_LOOP_WEIGHT_SCALE; if (!fgDominate(curBlk, endBlk)) { - weight *= 2; - } - else - { - /* Merging of blocks can disturb the Dominates - information (see RAID #46649) */ - if (weight < BB_LOOP_WEIGHT_SCALE) - { - weight *= 2; - } + scale *= 2; } - // We can overflow here so check for it - if (weight < curBlk->bbWeight) - { - weight = BB_MAX_WEIGHT; - } - - assert(curBlk->bbWeight != BB_ZERO_WEIGHT); - BasicBlock::weight_t scale = weight / curBlk->bbWeight; - curBlk->scaleBBWeight(scale); } -#ifdef DEBUG - if (verbose) - { - printf("\n " FMT_BB "(wt=%s)", curBlk->bbNum, refCntWtd2str(curBlk->getBBWeight(this))); - } -#endif + JITDUMP("\n " FMT_BB "(wt=" FMT_WT ")", curBlk->bbNum, curBlk->getBBWeight(this)); } + /* Stop if we've reached the last block in the loop */ if (curBlk == endBlk) @@ -1836,7 +1805,7 @@ class LoopSearch // Seed the loop block set and worklist with the entry block. loopBlocks.Reset(entry->bbNum); - jitstd::list worklist(comp->getAllocator()); + jitstd::list worklist(comp->getAllocator(CMK_LoopOpt)); worklist.push_back(entry); while (!worklist.empty()) @@ -3812,7 +3781,7 @@ void Compiler::optUnrollLoops() /* Create the unrolled loop statement list */ { - BlockToBlockMap blockMap(getAllocator()); + BlockToBlockMap blockMap(getAllocator(CMK_LoopOpt)); BasicBlock* insertAfter = bottom; for (lval = lbeg; totalIter; totalIter--) @@ -4068,58 +4037,78 @@ bool Compiler::optReachWithoutCall(BasicBlock* topBB, BasicBlock* botBB) return true; } +// static +Compiler::fgWalkResult Compiler::optInvertCountTreeInfo(GenTree** pTree, fgWalkData* data) +{ + OptInvertCountTreeInfoType* o = (OptInvertCountTreeInfoType*)data->pCallbackData; + + if (Compiler::IsSharedStaticHelper(*pTree)) + { + o->sharedStaticHelperCount += 1; + } + + if ((*pTree)->OperGet() == GT_ARR_LENGTH) + { + o->arrayLengthCount += 1; + } + + return WALK_CONTINUE; +} + //----------------------------------------------------------------------------- // optInvertWhileLoop: modify flow and duplicate code so that for/while loops are // entered at top and tested at bottom (aka loop rotation or bottom testing). +// Creates a "zero trip test" condition which guards entry to the loop. +// Enables loop invariant hoisting and loop cloning, which depend on +// `do {} while` format loops. Enables creation of a pre-header block after the +// zero trip test to place code that only runs if the loop is guaranteed to +// run at least once. // // Arguments: // block -- block that may be the predecessor of the un-rotated loop's test block. // // Notes: -// Optimizes "jmp C; do{} C:while(cond);" loops to "if (cond){ do{}while(cond}; }" -// Does not modify every loop +// Uses a simple lexical screen to detect likely loops. +// +// Specifically, we're looking for the following case: +// +// ... +// jmp test // `block` argument +// loop: +// ... +// ... +// test: +// ..stmts.. +// cond +// jtrue loop +// +// If we find this, and the condition is simple enough, we change +// the loop to the following: +// +// ... +// ..stmts.. // duplicated cond block statments +// cond // duplicated cond +// jfalse done +// // else fall-through +// loop: +// ... +// ... +// test: +// ..stmts.. +// cond +// jtrue loop +// done: // // Makes no changes if the flow pattern match fails. // // May not modify a loop if profile is unfavorable, if the cost of duplicating -// code is large (factoring in potential CSEs) +// code is large (factoring in potential CSEs). // void Compiler::optInvertWhileLoop(BasicBlock* block) { assert(opts.OptimizationEnabled()); assert(compCodeOpt() != SMALL_CODE); - /* - Optimize while loops into do { } while loop - Our loop hoisting logic requires do { } while loops. - Specifically, we're looking for the following case: - - ... - jmp test - loop: - ... - ... - test: - cond - jtrue loop - - If we find this, and the condition is simple enough, we change - the loop to the following: - - ... - cond - jfalse done - // else fall-through - loop: - ... - ... - test: - cond - jtrue loop - done: - - */ - /* Does the BB end with an unconditional jump? */ if (block->bbJumpKind != BBJ_ALWAYS || (block->bbFlags & BBF_KEEP_BBJ_ALWAYS)) @@ -4168,49 +4157,47 @@ void Compiler::optInvertWhileLoop(BasicBlock* block) return; } - // Find the loop termination test at the bottom of the loop. - Statement* condStmt = bTest->lastStmt(); - - // bTest must only contain only a jtrue with no other stmts, we will only clone - // the conditional, so any other statements will not get cloned - // TODO-CQ: consider cloning the whole bTest block and inserting it after block. + // It has to be a forward jump. Defer this check until after all the cheap checks + // are done, since it iterates forward in the block list looking for bbJumpDest. + // TODO-CQ: Check if we can also optimize the backwards jump as well. // - if (bTest->bbStmtList != condStmt) + if (!fgIsForwardBranch(block)) { return; } - /* Get to the condition node from the statement tree */ + // Find the loop termination test at the bottom of the loop. + Statement* condStmt = bTest->lastStmt(); - GenTree* condTree = condStmt->GetRootNode(); + // Verify the test block ends with a conditional that we can manipulate. + GenTree* const condTree = condStmt->GetRootNode(); noway_assert(condTree->gtOper == GT_JTRUE); - - condTree = condTree->AsOp()->gtOp1; - - // The condTree has to be a RelOp comparison - // TODO-CQ: Check if we can also optimize the backwards jump as well. - // - if (condTree->OperIsCompare() == false) + if (!condTree->AsOp()->gtOp1->OperIsCompare()) { return; } - // It has to be a forward jump. Defer this check until after all the cheap checks - // are done, since it iterates forward in the block list looking for bbJumpDest. - // TODO-CQ: Check if we can also optimize the backwards jump as well. + // Estimate the cost of cloning the entire test block. // - if (fgIsForwardBranch(block) == false) - { - return; - } - - /* We call gtPrepareCost to measure the cost of duplicating this tree */ + // Note: it would help throughput to compute the maximum cost + // first and early out for large bTest blocks, as we are doing two + // tree walks per tree. But because of this helper call scan, the + // maximum cost depends on the trees in the block. + // + // We might consider flagging blocks with hoistable helper calls + // during importation, so we can avoid the helper search and + // implement an early bail out for large blocks with no helper calls. - gtPrepareCost(condTree); - unsigned estDupCostSz = condTree->GetCostSz(); + unsigned estDupCostSz = 0; - BasicBlock::weight_t loopIterations = BB_LOOP_WEIGHT_SCALE; + for (Statement* stmt : bTest->Statements()) + { + GenTree* tree = stmt->GetRootNode(); + gtPrepareCost(tree); + estDupCostSz += tree->GetCostSz(); + } + BasicBlock::weight_t loopIterations = BB_LOOP_WEIGHT_SCALE; bool allProfileWeightsAreValid = false; BasicBlock::weight_t const weightBlock = block->bbWeight; BasicBlock::weight_t const weightTest = bTest->bbWeight; @@ -4259,10 +4246,8 @@ void Compiler::optInvertWhileLoop(BasicBlock* block) } } - unsigned maxDupCostSz = 32; + unsigned maxDupCostSz = 34; - // optFastCodeOrBlendedLoop(bTest->bbWeight) does not work here as we have not - // set loop weights yet if ((compCodeOpt() == FAST_CODE) || compStressCompile(STRESS_DO_WHILE_LOOPS, 30)) { maxDupCostSz *= 4; @@ -4278,40 +4263,65 @@ void Compiler::optInvertWhileLoop(BasicBlock* block) } } - // If the compare has too high cost then we don't want to dup + // If the compare has too high cost then we don't want to dup. bool costIsTooHigh = (estDupCostSz > maxDupCostSz); - int countOfHelpers = 0; + OptInvertCountTreeInfoType optInvertTotalInfo = {}; if (costIsTooHigh) { // If we already know that the cost is acceptable, then don't waste time walking the tree - // counting shared static helpers. + // counting things to boost the maximum allowed cost. // // If the loop condition has a shared static helper, we really want this loop converted // as not converting the loop will disable loop hoisting, meaning the shared helper will // be executed on every loop iteration. - fgWalkTreePre(&condTree, CountSharedStaticHelper, &countOfHelpers); + // + // If the condition has array.Length operations, also boost, as they are likely to be CSE'd. - if (countOfHelpers > 0) + for (Statement* stmt : bTest->Statements()) { - maxDupCostSz += 24 * min(countOfHelpers, (int)(loopIterations + 1.5)); + GenTree* tree = stmt->GetRootNode(); + + OptInvertCountTreeInfoType optInvertInfo = {}; + fgWalkTreePre(&tree, Compiler::optInvertCountTreeInfo, &optInvertInfo); + optInvertTotalInfo.sharedStaticHelperCount += optInvertInfo.sharedStaticHelperCount; + optInvertTotalInfo.arrayLengthCount += optInvertInfo.arrayLengthCount; - // Is the cost too high now? - costIsTooHigh = (estDupCostSz > maxDupCostSz); + if ((optInvertInfo.sharedStaticHelperCount > 0) || (optInvertInfo.arrayLengthCount > 0)) + { + // Calculate a new maximum cost. We might be able to early exit. + + unsigned newMaxDupCostSz = + maxDupCostSz + 24 * min(optInvertTotalInfo.sharedStaticHelperCount, (int)(loopIterations + 1.5)) + + 8 * optInvertTotalInfo.arrayLengthCount; + + // Is the cost too high now? + costIsTooHigh = (estDupCostSz > newMaxDupCostSz); + if (!costIsTooHigh) + { + // No need counting any more trees; we're going to do the transformation. + JITDUMP("Decided to duplicate loop condition block after counting helpers in tree [%06u] in " + "block " FMT_BB, + dspTreeID(tree), bTest->bbNum); + maxDupCostSz = newMaxDupCostSz; // for the JitDump output below + break; + } + } } } #ifdef DEBUG if (verbose) { - // Note that `countOfHelpers = 0` means either there were zero helpers, or the tree walk to count them was not - // done. - printf("\nDuplication of loop condition [%06u] is %s, because the cost of duplication (%i) is %s than %i," - "\n loopIterations = %7.3f, countOfHelpers = %d, validProfileWeights = %s\n", - dspTreeID(condTree), costIsTooHigh ? "not done" : "performed", estDupCostSz, - costIsTooHigh ? "greater" : "less or equal", maxDupCostSz, loopIterations, countOfHelpers, - dspBool(allProfileWeightsAreValid)); + // Note that `optInvertTotalInfo.sharedStaticHelperCount = 0` means either there were zero helpers, or the + // tree walk to count them was not done. + printf( + "\nDuplication of loop condition [%06u] is %s, because the cost of duplication (%i) is %s than %i," + "\n loopIterations = %7.3f, optInvertTotalInfo.sharedStaticHelperCount >= %d, validProfileWeights = %s\n", + dspTreeID(condTree), costIsTooHigh ? "not done" : "performed", estDupCostSz, + costIsTooHigh ? "greater" : "less or equal", maxDupCostSz, loopIterations, + optInvertTotalInfo.sharedStaticHelperCount, dspBool(allProfileWeightsAreValid)); } #endif @@ -4320,34 +4330,47 @@ void Compiler::optInvertWhileLoop(BasicBlock* block) return; } - /* Looks good - duplicate the condition test */ + bool foundCondTree = false; - condTree->gtFlags |= GTF_RELOP_ZTT; + // Clone each statement in bTest and append to block. + for (Statement* stmt : bTest->Statements()) + { + GenTree* originalTree = stmt->GetRootNode(); + GenTree* clonedTree = gtCloneExpr(originalTree); - condTree = gtCloneExpr(condTree); - gtReverseCond(condTree); + // Special case handling needed for the conditional jump tree + if (originalTree == condTree) + { + foundCondTree = true; - // Make sure clone expr copied the flag - assert(condTree->gtFlags & GTF_RELOP_ZTT); + // Get the compare subtrees + GenTree* originalCompareTree = originalTree->AsOp()->gtOp1; + GenTree* clonedCompareTree = clonedTree->AsOp()->gtOp1; + assert(originalCompareTree->OperIsCompare()); + assert(clonedCompareTree->OperIsCompare()); - condTree = gtNewOperNode(GT_JTRUE, TYP_VOID, condTree); + // Flag compare and cloned copy so later we know this loop + // has a proper zero trip test. + originalCompareTree->gtFlags |= GTF_RELOP_ZTT; + clonedCompareTree->gtFlags |= GTF_RELOP_ZTT; - /* Create a statement entry out of the condition and - append the condition test at the end of 'block' */ + // The original test branches to remain in the loop. The + // new cloned test will branch to avoid the loop. So the + // cloned compare needs to reverse the branch condition. + gtReverseCond(clonedCompareTree); + } - Statement* copyOfCondStmt = fgNewStmtAtEnd(block, condTree); + Statement* clonedStmt = fgNewStmtAtEnd(block, clonedTree); - copyOfCondStmt->SetCompilerAdded(); + if (opts.compDbgInfo) + { + clonedStmt->SetILOffsetX(stmt->GetILOffsetX()); + } - if (condTree->gtFlags & GTF_CALL) - { - block->bbFlags |= BBF_HAS_CALL; // Record that the block has a call + clonedStmt->SetCompilerAdded(); } - if (opts.compDbgInfo) - { - copyOfCondStmt->SetILOffsetX(condStmt->GetILOffsetX()); - } + assert(foundCondTree); // Flag the block that received the copy as potentially having an array/vtable // reference, nullcheck, object/array allocation if the block copied from did; @@ -4372,17 +4395,6 @@ void Compiler::optInvertWhileLoop(BasicBlock* block) fgRemoveRefPred(bTest, block); fgAddRefPred(bTest->bbNext, block); -#ifdef DEBUG - if (verbose) - { - printf("\nDuplicated loop condition in " FMT_BB " for loop (" FMT_BB " - " FMT_BB ")\n", block->bbNum, - block->bbNext->bbNum, bTest->bbNum); - printf("Estimated code size expansion is %d\n", estDupCostSz); - - gtDispStmt(copyOfCondStmt); - } -#endif // DEBUG - // If we have profile data for all blocks and we know that we are cloning the // bTest block into block and thus changing the control flow from block so // that it no longer goes directly to bTest anymore, we have to adjust @@ -4451,6 +4463,18 @@ void Compiler::optInvertWhileLoop(BasicBlock* block) fgDebugCheckIncomingProfileData(block->bbJumpDest); #endif // DEBUG } + +#ifdef DEBUG + if (verbose) + { + printf("\nDuplicated loop exit block at " FMT_BB " for loop (" FMT_BB " - " FMT_BB ")\n", block->bbNum, + block->bbNext->bbNum, bTest->bbNum); + printf("Estimated code size expansion is %d\n", estDupCostSz); + + fgDumpBlock(block); + fgDumpBlock(bTest); + } +#endif // DEBUG } //----------------------------------------------------------------------------- @@ -4464,6 +4488,14 @@ PhaseStatus Compiler::optInvertLoops() noway_assert(opts.OptimizationEnabled()); noway_assert(fgModified == false); +#if defined(OPT_CONFIG) + if (!JitConfig.JitDoLoopInversion()) + { + JITDUMP("Loop inversion disabled\n"); + return PhaseStatus::MODIFIED_NOTHING; + } +#endif // OPT_CONFIG + if (compCodeOpt() == SMALL_CODE) { return PhaseStatus::MODIFIED_NOTHING; @@ -4642,6 +4674,7 @@ PhaseStatus Compiler::optFindLoops() // Check if any of the loops need alignment + JITDUMP("\n"); optIdentifyLoopsForAlignment(); #if COUNT_LOOPS @@ -4756,7 +4789,7 @@ bool Compiler::optDeriveLoopCloningConditions(unsigned loopNum, LoopCloneContext } else if (loop->lpFlags & LPFLG_ARRLEN_LIMIT) { - ArrIndex* index = new (getAllocator()) ArrIndex(getAllocator()); + ArrIndex* index = new (getAllocator(CMK_LoopClone)) ArrIndex(getAllocator(CMK_LoopClone)); if (!loop->lpArrLenLimit(this, index)) { JITDUMP("> ArrLen not matching"); @@ -4799,8 +4832,8 @@ bool Compiler::optDeriveLoopCloningConditions(unsigned loopNum, LoopCloneContext // limit <= mdArrLen LcMdArrayOptInfo* mdArrInfo = optInfo->AsLcMdArrayOptInfo(); LC_Condition cond(GT_LE, LC_Expr(ident), - LC_Expr(LC_Ident(LC_Array(LC_Array::MdArray, - mdArrInfo->GetArrIndexForDim(getAllocator()), + LC_Expr(LC_Ident(LC_Array(LC_Array::MdArray, mdArrInfo->GetArrIndexForDim( + getAllocator(CMK_LoopClone)), mdArrInfo->dim, LC_Array::None)))); context->EnsureConditions(loopNum)->Push(cond); } @@ -4926,7 +4959,7 @@ bool Compiler::optDeriveLoopCloningConditions(unsigned loopNum, LoopCloneContext // bool Compiler::optComputeDerefConditions(unsigned loopNum, LoopCloneContext* context) { - JitExpandArrayStack nodes(getAllocator()); + JitExpandArrayStack nodes(getAllocator(CMK_LoopClone)); int maxRank = -1; // Get the dereference-able arrays. @@ -4944,7 +4977,7 @@ bool Compiler::optComputeDerefConditions(unsigned loopNum, LoopCloneContext* con LC_Deref* node = LC_Deref::Find(&nodes, array.arrIndex->arrLcl); if (node == nullptr) { - node = new (getAllocator()) LC_Deref(array, 0 /*level*/); + node = new (getAllocator(CMK_LoopClone)) LC_Deref(array, 0 /*level*/); nodes.Push(node); } @@ -4953,11 +4986,11 @@ bool Compiler::optComputeDerefConditions(unsigned loopNum, LoopCloneContext* con unsigned rank = (unsigned)array.GetDimRank(); for (unsigned i = 0; i < rank; ++i) { - node->EnsureChildren(getAllocator()); + node->EnsureChildren(getAllocator(CMK_LoopClone)); LC_Deref* tmp = node->Find(array.arrIndex->indLcls[i]); if (tmp == nullptr) { - tmp = new (getAllocator()) LC_Deref(array, node->level + 1); + tmp = new (getAllocator(CMK_LoopClone)) LC_Deref(array, node->level + 1); node->children->Push(tmp); } @@ -5300,7 +5333,7 @@ void Compiler::optCloneLoops() unsigned optStaticallyOptimizedLoops = 0; - LoopCloneContext context(optLoopCount, getAllocator()); + LoopCloneContext context(optLoopCount, getAllocator(CMK_LoopClone)); // Obtain array optimization candidates in the context. optObtainLoopCloningOpts(&context); @@ -5508,7 +5541,7 @@ void Compiler::optCloneLoop(unsigned loopInd, LoopCloneContext* context) BasicBlock* newFirst = nullptr; BasicBlock* newBot = nullptr; - BlockToBlockMap* blockMap = new (getAllocator()) BlockToBlockMap(getAllocator()); + BlockToBlockMap* blockMap = new (getAllocator(CMK_LoopClone)) BlockToBlockMap(getAllocator(CMK_LoopClone)); for (BasicBlock* blk = loop.lpFirst; blk != loop.lpBottom->bbNext; blk = blk->bbNext) { BasicBlock* newBlk = fgNewBBafter(blk->bbJumpKind, newPred, /*extendRegion*/ true); @@ -5723,7 +5756,7 @@ void Compiler::optEnsureUniqueHead(unsigned loopInd, BasicBlock::weight_t ambien BlockSetOps::Assign(this, h2->bbReach, e->bbReach); // Redirect paths from preds of "e" to go to "h2" instead of "e". - BlockToBlockMap* blockMap = new (getAllocator()) BlockToBlockMap(getAllocator()); + BlockToBlockMap* blockMap = new (getAllocator(CMK_LoopClone)) BlockToBlockMap(getAllocator(CMK_LoopClone)); blockMap->Set(e, h2); for (flowList* predEntry = e->bbPreds; predEntry; predEntry = predEntry->flNext) @@ -8851,7 +8884,7 @@ bool Compiler::optIsStackLocalInvariant(unsigned loopNum, unsigned lclNum) // Compiler::fgWalkResult Compiler::optCanOptimizeByLoopCloning(GenTree* tree, LoopCloneVisitorInfo* info) { - ArrIndex arrIndex(getAllocator()); + ArrIndex arrIndex(getAllocator(CMK_LoopClone)); // Check if array index can be optimized. if (optReconstructArrIndex(tree, &arrIndex, BAD_VAR_NUM)) diff --git a/src/coreclr/jit/phase.cpp b/src/coreclr/jit/phase.cpp index b56d29b2b519e0..cc9d6466d73e13 100644 --- a/src/coreclr/jit/phase.cpp +++ b/src/coreclr/jit/phase.cpp @@ -197,10 +197,6 @@ void Phase::PostPhase(PhaseStatus status) printf("Trees after %s\n", m_name); comp->fgDispBasicBlocks(true); } - -#if DUMP_FLOWGRAPHS - comp->fgDumpFlowGraph(m_phase); -#endif // DUMP_FLOWGRAPHS } if (doPostPhase) @@ -230,5 +226,9 @@ void Phase::PostPhase(PhaseStatus status) #endif // DEBUG +#if DUMP_FLOWGRAPHS + comp->fgDumpFlowGraph(m_phase); +#endif // DUMP_FLOWGRAPHS + comp->EndPhase(m_phase); } diff --git a/src/coreclr/jit/unwind.cpp b/src/coreclr/jit/unwind.cpp index 7bbccd789d6c52..14dc49f50fac6c 100644 --- a/src/coreclr/jit/unwind.cpp +++ b/src/coreclr/jit/unwind.cpp @@ -178,7 +178,7 @@ void Compiler::unwindBegPrologCFI() unwindGetFuncLocations(func, false, &func->coldStartLoc, &func->coldEndLoc); } - func->cfiCodes = new (getAllocator()) CFICodeVector(getAllocator()); + func->cfiCodes = new (getAllocator(CMK_UnwindInfo)) CFICodeVector(getAllocator()); #endif // FEATURE_EH_FUNCLETS } diff --git a/src/coreclr/jit/utils.cpp b/src/coreclr/jit/utils.cpp index 26914f10112f9f..1afb1e78fb0f56 100644 --- a/src/coreclr/jit/utils.cpp +++ b/src/coreclr/jit/utils.cpp @@ -657,7 +657,7 @@ const char* refCntWtd2str(BasicBlock::weight_t refCntWtd) nump = (nump == num1) ? num2 : num1; - if (refCntWtd == BB_MAX_WEIGHT) + if (refCntWtd >= BB_MAX_WEIGHT) { sprintf_s(temp, bufSize, "MAX "); } diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index 2d7e127666ba2b..3957ff13c34700 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -5847,8 +5847,8 @@ struct ValueNumberState } ValueNumberState(Compiler* comp) - : m_toDoAllPredsDone(comp->getAllocator(), /*minSize*/ 4) - , m_toDoNotAllPredsDone(comp->getAllocator(), /*minSize*/ 4) + : m_toDoAllPredsDone(comp->getAllocator(CMK_ValueNumber), /*minSize*/ 4) + , m_toDoNotAllPredsDone(comp->getAllocator(CMK_ValueNumber), /*minSize*/ 4) , m_comp(comp) , m_visited(new (comp, CMK_ValueNumber) BYTE[comp->fgBBNumMax + 1]()) { diff --git a/src/coreclr/pal/src/cruntime/printf.cpp b/src/coreclr/pal/src/cruntime/printf.cpp index 79d16e7b48f912..f08f6475daa96f 100644 --- a/src/coreclr/pal/src/cruntime/printf.cpp +++ b/src/coreclr/pal/src/cruntime/printf.cpp @@ -303,7 +303,7 @@ static BOOL Internal_ScanfExtractFormatW(LPCWSTR *Fmt, LPSTR Out, int iOutSize, { ERROR("atoi returned a negative value indicative of an overflow.\n"); SetLastError(ERROR_INTERNAL_ERROR); - return Result; + goto EXIT; } } @@ -401,7 +401,7 @@ static BOOL Internal_ScanfExtractFormatW(LPCWSTR *Fmt, LPSTR Out, int iOutSize, { ERROR("strcpy_s failed\n"); SetLastError(ERROR_INSUFFICIENT_BUFFER); - return FALSE; + goto EXIT; } Out += strlen(scanf_longlongfmt); @@ -532,6 +532,8 @@ static BOOL Internal_ScanfExtractFormatW(LPCWSTR *Fmt, LPSTR Out, int iOutSize, *Out++ = 'n'; *Out = 0; /* end the string */ + +EXIT: PAL_free(TempStr); return Result; } diff --git a/src/coreclr/pal/src/cruntime/printfcpp.cpp b/src/coreclr/pal/src/cruntime/printfcpp.cpp index 0f7cb682650f80..d7e992c090a00b 100644 --- a/src/coreclr/pal/src/cruntime/printfcpp.cpp +++ b/src/coreclr/pal/src/cruntime/printfcpp.cpp @@ -220,7 +220,7 @@ BOOL Internal_ExtractFormatA(CPalThread *pthrCurrent, LPCSTR *Fmt, LPSTR Out, LP { ERROR("atoi returned a negative value indicative of an overflow.\n"); pthrCurrent->SetLastError(ERROR_INTERNAL_ERROR); - return Result; + goto EXIT; } } else if (**Fmt == '*') @@ -258,7 +258,7 @@ BOOL Internal_ExtractFormatA(CPalThread *pthrCurrent, LPCSTR *Fmt, LPSTR Out, LP { ERROR("atoi returned a negative value indicative of an overflow.\n"); pthrCurrent->SetLastError(ERROR_INTERNAL_ERROR); - return Result; + goto EXIT; } } else if (**Fmt == '*') @@ -444,6 +444,8 @@ BOOL Internal_ExtractFormatA(CPalThread *pthrCurrent, LPCSTR *Fmt, LPSTR Out, LP } *Out = 0; /* end the string */ + +EXIT: free(TempStr); return Result; } @@ -525,7 +527,7 @@ BOOL Internal_ExtractFormatW(CPalThread *pthrCurrent, LPCWSTR *Fmt, LPSTR Out, L { ERROR("atoi returned a negative value indicative of an overflow.\n"); pthrCurrent->SetLastError(ERROR_INTERNAL_ERROR); - return Result; + goto EXIT; } } else if (**Fmt == '*') @@ -562,7 +564,7 @@ BOOL Internal_ExtractFormatW(CPalThread *pthrCurrent, LPCWSTR *Fmt, LPSTR Out, L { ERROR("atoi returned a negative value indicative of an overflow.\n"); pthrCurrent->SetLastError(ERROR_INTERNAL_ERROR); - return Result; + goto EXIT; } } else if (**Fmt == '*') @@ -772,6 +774,8 @@ BOOL Internal_ExtractFormatW(CPalThread *pthrCurrent, LPCWSTR *Fmt, LPSTR Out, L } *Out = 0; /* end the string */ + +EXIT: free(TempStr); return Result; } @@ -899,7 +903,7 @@ static INT Internal_AddPaddingVfwprintf(CPalThread *pthrCurrent, PAL_FILE *strea LPWSTR OutOriginal; INT LengthInStr; INT Length; - INT Written = 0; + INT Written = -1; LengthInStr = PAL_wcslen(In); Length = LengthInStr; @@ -924,9 +928,8 @@ static INT Internal_AddPaddingVfwprintf(CPalThread *pthrCurrent, PAL_FILE *strea if (wcscpy_s(Out, iLen, In) != SAFECRT_SUCCESS) { ERROR("wcscpy_s failed!\n"); - free(OutOriginal); pthrCurrent->SetLastError(ERROR_INSUFFICIENT_BUFFER); - return -1; + goto EXIT; } Out += LengthInStr; iLen -= LengthInStr; @@ -954,9 +957,8 @@ static INT Internal_AddPaddingVfwprintf(CPalThread *pthrCurrent, PAL_FILE *strea if (wcscpy_s(Out, iLen, In) != SAFECRT_SUCCESS) { ERROR("wcscpy_s failed!\n"); - free(OutOriginal); pthrCurrent->SetLastError(ERROR_INSUFFICIENT_BUFFER); - return -1; + goto EXIT; } Out += LengthInStr; @@ -971,9 +973,14 @@ static INT Internal_AddPaddingVfwprintf(CPalThread *pthrCurrent, PAL_FILE *strea { ERROR("fwrite() failed with errno == %d\n", errno); } - free(OutOriginal); + } + else + { + Written = 0; } +EXIT: + free(OutOriginal); return Written; } diff --git a/src/coreclr/pal/src/cruntime/silent_printf.cpp b/src/coreclr/pal/src/cruntime/silent_printf.cpp index bc9c718fe3ae3a..17e3007c762fc2 100644 --- a/src/coreclr/pal/src/cruntime/silent_printf.cpp +++ b/src/coreclr/pal/src/cruntime/silent_printf.cpp @@ -402,7 +402,7 @@ BOOL Silent_ExtractFormatA(LPCSTR *Fmt, LPSTR Out, LPINT Flags, LPINT Width, LPI if (*Width < 0) { SetLastError(ERROR_INTERNAL_ERROR); - return Result; + goto EXIT; } } else if (**Fmt == '*') @@ -439,7 +439,7 @@ BOOL Silent_ExtractFormatA(LPCSTR *Fmt, LPSTR Out, LPINT Flags, LPINT Width, LPI if (*Precision < 0) { SetLastError(ERROR_INTERNAL_ERROR); - return Result; + goto EXIT; } } else if (**Fmt == '*') @@ -595,6 +595,8 @@ BOOL Silent_ExtractFormatA(LPCSTR *Fmt, LPSTR Out, LPINT Flags, LPINT Width, LPI } *Out = 0; /* end the string */ + +EXIT: PAL_free(TempStr); return Result; } diff --git a/src/coreclr/pal/src/map/virtual.cpp b/src/coreclr/pal/src/map/virtual.cpp index 456254bdbbb9a8..cea55e86e2538f 100644 --- a/src/coreclr/pal/src/map/virtual.cpp +++ b/src/coreclr/pal/src/map/virtual.cpp @@ -1765,8 +1765,8 @@ bool PAL_JITWriteEnableHolder::JITWriteEnable(bool writeEnable) { // Use a thread local to track per thread JIT Write enable state - // Initialize threads to start with MAP_JIT pages readable and executable (R-X) by default. - thread_local bool enabled = (pthread_jit_write_protect_np(1), false); + // Per Apple, new threads start with MAP_JIT pages readable and executable (R-X) by default. + thread_local bool enabled = false; bool result = enabled; if (enabled != writeEnable) { diff --git a/src/coreclr/scripts/superpmi.py b/src/coreclr/scripts/superpmi.py index a98d3bbe0beb9c..541db5696271e1 100755 --- a/src/coreclr/scripts/superpmi.py +++ b/src/coreclr/scripts/superpmi.py @@ -2595,7 +2595,7 @@ def process_mch_files_arg(coreclr_args): # See if the cache directory already exists. If so, we just use it (unless `--force_download` is passed). if os.path.isdir(mch_cache_dir) and not coreclr_args.force_download: - # The cache directory is already there, and "--force_download" was passed, so just + # The cache directory is already there, and "--force_download" was not passed, so just # assume it's got what we want. # NOTE: a different solution might be to verify that everything we would download is # already in the cache, and simply not download if it is. However, that would @@ -2608,7 +2608,12 @@ def process_mch_files_arg(coreclr_args): # Add the private store files if coreclr_args.private_store is not None: - local_mch_paths += process_local_mch_files(coreclr_args, coreclr_args.private_store, mch_cache_dir) + # Only include the directories corresponding to the current JIT/EE version, target OS, and MCH architecture (this is the + # same filtering done for Azure storage). Only include them if they actually exist (e.g., the private store might have + # windows x64 but not Linux arm). + target_specific_stores = [ os.path.abspath(os.path.join(store, coreclr_args.jit_ee_version, coreclr_args.target_os, coreclr_args.mch_arch)) for store in coreclr_args.private_store ] + filtered_stores = [ s for s in target_specific_stores if os.path.isdir(s) ] + local_mch_paths += process_local_mch_files(coreclr_args, filtered_stores, mch_cache_dir) return local_mch_paths diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index d99c51d236950c..c138b9500c8822 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -654,6 +654,7 @@ private bool TryGetUnmanagedCallingConventionFromModOpt(MethodSignature signatur if (memberFunctionVariant) { callConv = GetMemberFunctionCallingConventionVariant(found ? callConv : (CorInfoCallConvExtension)PlatformDefaultUnmanagedCallingConvention()); + found = true; } return found; @@ -1579,8 +1580,7 @@ private int appendClassName(char** ppBuf, ref int pnBufLen, CORINFO_CLASS_STRUCT private bool isValueClass(CORINFO_CLASS_STRUCT_* cls) { - TypeDesc type = HandleToObject(cls); - return type.IsValueType || type.IsPointer || type.IsFunctionPointer; + return HandleToObject(cls).IsValueType; } private CorInfoInlineTypeCheck canInlineTypeCheck(CORINFO_CLASS_STRUCT_* cls, CorInfoInlineTypeCheckSource source) @@ -1602,10 +1602,6 @@ private uint getClassAttribsInternal(TypeDesc type) CorInfoFlag result = (CorInfoFlag)0; - // CoreCLR uses UIntPtr in place of pointers here - if (type.IsPointer || type.IsFunctionPointer) - type = _compilation.TypeSystemContext.GetWellKnownType(WellKnownType.UIntPtr); - var metadataType = type as MetadataType; // The array flag is used to identify the faked-up methods on diff --git a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaModule.cs b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaModule.cs index 737b266b9b6e62..1e923b2d23fe1f 100644 --- a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaModule.cs +++ b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaModule.cs @@ -595,7 +595,10 @@ private Object ResolveExportedType(ExportedTypeHandle handle) var module = (ModuleDesc)implementation; string nameSpace = _metadataReader.GetString(exportedType.Namespace); string name = _metadataReader.GetString(exportedType.Name); - return module.GetType(nameSpace, name, NotFoundBehavior.ReturnResolutionFailure); + MetadataType resolvedType = module.GetType(nameSpace, name, NotFoundBehavior.ReturnResolutionFailure); + if (resolvedType == null) + return ModuleDesc.GetTypeResolutionFailure; + return resolvedType; } else if (implementation is MetadataType) diff --git a/src/coreclr/vm/assemblyloadcontext.cpp b/src/coreclr/vm/assemblyloadcontext.cpp index 1ec1a1eeeb6c85..565292d7fb3d9f 100644 --- a/src/coreclr/vm/assemblyloadcontext.cpp +++ b/src/coreclr/vm/assemblyloadcontext.cpp @@ -24,12 +24,17 @@ NativeImage *AssemblyLoadContext::LoadNativeImage(Module *componentModule, LPCUT AssemblyLoadContext *loadContext = componentModule->GetFile()->GetAssemblyLoadContext(); PTR_LoaderAllocator moduleLoaderAllocator = componentModule->GetLoaderAllocator(); - NativeImage *nativeImage = NativeImage::Open(componentModule, nativeImageName, loadContext, moduleLoaderAllocator); - m_nativeImages.Append(nativeImage); + bool isNewNativeImage; + NativeImage *nativeImage = NativeImage::Open(componentModule, nativeImageName, loadContext, moduleLoaderAllocator, &isNewNativeImage); - for (COUNT_T assemblyIndex = 0; assemblyIndex < m_loadedAssemblies.GetCount(); assemblyIndex++) + if (isNewNativeImage && nativeImage != nullptr) { - nativeImage->CheckAssemblyMvid(m_loadedAssemblies[assemblyIndex]); + m_nativeImages.Append(nativeImage); + + for (COUNT_T assemblyIndex = 0; assemblyIndex < m_loadedAssemblies.GetCount(); assemblyIndex++) + { + nativeImage->CheckAssemblyMvid(m_loadedAssemblies[assemblyIndex]); + } } return nativeImage; diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 2423c4887d61e5..dc8bdb96db7c94 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -3772,11 +3772,7 @@ bool CEEInfo::isValueClass(CORINFO_CLASS_HANDLE clsHnd) _ASSERTE(clsHnd); - // Note that clsHnd.IsValueType() would not return what the JIT expects - // for corner cases like ELEMENT_TYPE_FNPTR - TypeHandle VMClsHnd(clsHnd); - MethodTable * pMT = VMClsHnd.GetMethodTable(); - ret = (pMT != NULL) ? pMT->IsValueType() : false; + ret = TypeHandle(clsHnd).IsValueType(); EE_TO_JIT_TRANSITION_LEAF(); @@ -3888,7 +3884,7 @@ uint32_t CEEInfo::getClassAttribsInternal (CORINFO_CLASS_HANDLE clsHnd) if (pMT->HasComponentSize()) ret |= CORINFO_FLG_VAROBJSIZE; - if (pMT->IsValueType()) + if (VMClsHnd.IsValueType()) { ret |= CORINFO_FLG_VALUECLASS; diff --git a/src/coreclr/vm/nativeimage.cpp b/src/coreclr/vm/nativeimage.cpp index 9067273ed2ec28..5500da1acc8726 100644 --- a/src/coreclr/vm/nativeimage.cpp +++ b/src/coreclr/vm/nativeimage.cpp @@ -114,13 +114,15 @@ NativeImage *NativeImage::Open( Module *componentModule, LPCUTF8 nativeImageFileName, AssemblyLoadContext *pAssemblyLoadContext, - LoaderAllocator *pLoaderAllocator) + LoaderAllocator *pLoaderAllocator, + /* out */ bool *isNewNativeImage) { STANDARD_VM_CONTRACT; NativeImage *pExistingImage = AppDomain::GetCurrentDomain()->GetNativeImage(nativeImageFileName); if (pExistingImage != nullptr) { + *isNewNativeImage = false; return pExistingImage->GetAssemblyLoadContext() == pAssemblyLoadContext ? pExistingImage : nullptr; } @@ -216,10 +218,12 @@ NativeImage *NativeImage::Open( if (pExistingImage == nullptr) { // No pre-existing image, new image has been stored in the map + *isNewNativeImage = true; amTracker.SuppressRelease(); return image.Extract(); } // Return pre-existing image if it was loaded into the same ALC, null otherwise + *isNewNativeImage = false; return (pExistingImage->GetAssemblyLoadContext() == pAssemblyLoadContext ? pExistingImage : nullptr); } #endif diff --git a/src/coreclr/vm/nativeimage.h b/src/coreclr/vm/nativeimage.h index 8ce2c43fe7fd11..447e600297ae3e 100644 --- a/src/coreclr/vm/nativeimage.h +++ b/src/coreclr/vm/nativeimage.h @@ -105,7 +105,8 @@ class NativeImage Module *componentModule, LPCUTF8 nativeImageFileName, AssemblyLoadContext *pAssemblyLoadContext, - LoaderAllocator *pLoaderAllocator); + LoaderAllocator *pLoaderAllocator, + /* out */ bool *isNewNativeImage); Crst *EagerFixupsLock() { return &m_eagerFixupsLock; } bool EagerFixupsHaveRun() const { return m_eagerFixupsHaveRun; } diff --git a/src/coreclr/vm/object.h b/src/coreclr/vm/object.h index c53ce1e88a1af3..5cad6621e4f4ca 100644 --- a/src/coreclr/vm/object.h +++ b/src/coreclr/vm/object.h @@ -739,6 +739,39 @@ class PtrArray : public ArrayBase #define OFFSETOF__PtrArray__m_Array_ ARRAYBASE_SIZE +/* Corresponds to the managed Span and ReadOnlySpan types. + This should only ever be passed from the managed to the unmanaged world byref, + as any copies of this struct made within the unmanaged world will not observe + potential GC relocations of the source data. */ +template < class KIND > +class Span +{ +private: + /* Keep fields below in sync with managed Span / ReadOnlySpan layout. */ + KIND* _pointer; + unsigned int _length; + +public: + // !! CAUTION !! + // Caller must take care not to reassign returned reference if this span corresponds + // to a managed ReadOnlySpan. If KIND is a reference type, caller must use a + // helper like SetObjectReference instead of assigning values directly to the + // reference location. + KIND& GetAt(SIZE_T index) + { + LIMITED_METHOD_CONTRACT; + SUPPORTS_DAC; + _ASSERTE(index < GetLength()); + return _pointer[index]; + } + + // Gets the length (in elements) of this span. + __inline SIZE_T GetLength() const + { + return _length; + } +}; + /* a TypedByRef is a structure that is used to implement VB's BYREF variants. it is basically a tuple of an address of some data along with a TypeHandle that indicates the type of the address */ diff --git a/src/coreclr/vm/reflectioninvocation.cpp b/src/coreclr/vm/reflectioninvocation.cpp index 3491dcb3489445..8a92ce9d7d9336 100644 --- a/src/coreclr/vm/reflectioninvocation.cpp +++ b/src/coreclr/vm/reflectioninvocation.cpp @@ -478,7 +478,7 @@ void CallDescrWorkerReflectionWrapper(CallDescrData * pCallDescrData, Frame * pF PAL_ENDTRY } // CallDescrWorkerReflectionWrapper -OBJECTREF InvokeArrayConstructor(TypeHandle th, MethodDesc* pMeth, PTRARRAYREF* objs, int argCnt) +OBJECTREF InvokeArrayConstructor(TypeHandle th, MethodDesc* pMeth, Span* objs, int argCnt) { CONTRACTL { THROWS; @@ -501,16 +501,16 @@ OBJECTREF InvokeArrayConstructor(TypeHandle th, MethodDesc* pMeth, PTRARRAYREF* for (DWORD i=0; i<(DWORD)argCnt; i++) { - if (!(*objs)->m_Array[i]) + if (!objs->GetAt(i)) COMPlusThrowArgumentException(W("parameters"), W("Arg_NullIndex")); - MethodTable* pMT = ((*objs)->m_Array[i])->GetMethodTable(); + MethodTable* pMT = objs->GetAt(i)->GetMethodTable(); CorElementType oType = TypeHandle(pMT).GetVerifierCorElementType(); if (!InvokeUtil::IsPrimitiveType(oType) || !InvokeUtil::CanPrimitiveWiden(ELEMENT_TYPE_I4,oType)) COMPlusThrow(kArgumentException,W("Arg_PrimWiden")); - memcpy(&indexes[i],(*objs)->m_Array[i]->UnBox(),pMT->GetNumInstanceFieldBytes()); + memcpy(&indexes[i], objs->GetAt(i)->UnBox(),pMT->GetNumInstanceFieldBytes()); } return AllocateArrayEx(th, indexes, argCnt); @@ -749,20 +749,18 @@ void DECLSPEC_NORETURN ThrowInvokeMethodException(MethodDesc * pMethod, OBJECTRE } FCIMPL5(Object*, RuntimeMethodHandle::InvokeMethod, - Object *target, PTRArray *objs, SignatureNative* pSigUNSAFE, + Object *target, Span* objs, SignatureNative* pSigUNSAFE, CLR_BOOL fConstructor, CLR_BOOL fWrapExceptions) { FCALL_CONTRACT; struct { OBJECTREF target; - PTRARRAYREF args; SIGNATURENATIVEREF pSig; OBJECTREF retVal; } gc; gc.target = ObjectToOBJECTREF(target); - gc.args = (PTRARRAYREF)objs; gc.pSig = (SIGNATURENATIVEREF)pSigUNSAFE; gc.retVal = NULL; @@ -793,7 +791,7 @@ FCIMPL5(Object*, RuntimeMethodHandle::InvokeMethod, if (ownerType.IsArray()) { gc.retVal = InvokeArrayConstructor(ownerType, pMeth, - &gc.args, + objs, gc.pSig->NumFixedArgs()); goto Done; } @@ -1044,7 +1042,7 @@ FCIMPL5(Object*, RuntimeMethodHandle::InvokeMethod, argDest = ArgDestination(pStackCopy, 0, NULL); } - InvokeUtil::CopyArg(th, &(gc.args->m_Array[i]), &argDest); + InvokeUtil::CopyArg(th, &objs->GetAt(i), &argDest); } ENDFORBIDGC(); @@ -1159,7 +1157,7 @@ FCIMPL5(Object*, RuntimeMethodHandle::InvokeMethod, while (byRefToNullables != NULL) { OBJECTREF obj = Nullable::Box(byRefToNullables->data, byRefToNullables->type.GetMethodTable()); - SetObjectReference(&gc.args->m_Array[byRefToNullables->argNum], obj); + SetObjectReference(&objs->GetAt(byRefToNullables->argNum), obj); byRefToNullables = byRefToNullables->next; } diff --git a/src/coreclr/vm/runtimehandles.h b/src/coreclr/vm/runtimehandles.h index 7675d1b3bcac9f..be3b2637e32366 100644 --- a/src/coreclr/vm/runtimehandles.h +++ b/src/coreclr/vm/runtimehandles.h @@ -270,7 +270,7 @@ class RuntimeMethodHandle { public: static FCDECL1(ReflectMethodObject*, GetCurrentMethod, StackCrawlMark* stackMark); - static FCDECL5(Object*, InvokeMethod, Object *target, PTRArray *objs, SignatureNative* pSig, CLR_BOOL fConstructor, CLR_BOOL fWrapExceptions); + static FCDECL5(Object*, InvokeMethod, Object *target, Span* objs, SignatureNative* pSig, CLR_BOOL fConstructor, CLR_BOOL fWrapExceptions); struct StreamingContextData { Object * additionalContext; // additionalContex was changed from OBJECTREF to Object to avoid having a diff --git a/src/installer/managed/Microsoft.NET.HostModel/ComHost/ComHost.cs b/src/installer/managed/Microsoft.NET.HostModel/ComHost/ComHost.cs index 2e1d8a949efb6e..3cf9cbf4e1004d 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/ComHost/ComHost.cs +++ b/src/installer/managed/Microsoft.NET.HostModel/ComHost/ComHost.cs @@ -2,7 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Generic; using System.IO; +using System.Runtime.InteropServices; using System.Text; namespace Microsoft.NET.HostModel.ComHost @@ -19,10 +21,12 @@ public class ComHost /// The path of Apphost template, which has the place holder /// The destination path for desired location to place, including the file name /// The path to the *.clsidmap file. + /// Resource ids for tlbs and paths to the tlb files to be embedded. public static void Create( string comHostSourceFilePath, string comHostDestinationFilePath, - string clsidmapFilePath) + string clsidmapFilePath, + IReadOnlyDictionary typeLibraries = null) { var destinationDirectory = new FileInfo(comHostDestinationFilePath).Directory.FullName; if (!Directory.Exists(destinationDirectory)) @@ -44,6 +48,26 @@ public static void Create( using (ResourceUpdater updater = new ResourceUpdater(comHostDestinationFilePath)) { updater.AddResource(clsidMapBytes, (IntPtr)ClsidmapResourceType, (IntPtr)ClsidmapResourceId); + if (typeLibraries is not null) + { + foreach (var typeLibrary in typeLibraries) + { + if (!ResourceUpdater.IsIntResource((IntPtr)typeLibrary.Key)) + { + throw new InvalidTypeLibraryIdException(typeLibrary.Value, typeLibrary.Key); + } + + try + { + byte[] tlbFileBytes = File.ReadAllBytes(typeLibrary.Value); + updater.AddResource(tlbFileBytes, "typelib", (IntPtr)typeLibrary.Key); + } + catch (FileNotFoundException ex) + { + throw new TypeLibraryDoesNotExistException(typeLibrary.Value, ex); + } + } + } updater.Update(); } } diff --git a/src/installer/managed/Microsoft.NET.HostModel/ComHost/InvalidTypeLibraryIdException.cs b/src/installer/managed/Microsoft.NET.HostModel/ComHost/InvalidTypeLibraryIdException.cs new file mode 100644 index 00000000000000..4bb1a85d4d42fc --- /dev/null +++ b/src/installer/managed/Microsoft.NET.HostModel/ComHost/InvalidTypeLibraryIdException.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Microsoft.NET.HostModel.ComHost +{ + /// + /// The provided resource id for the type library is unsupported. + /// + public class InvalidTypeLibraryIdException : Exception + { + public InvalidTypeLibraryIdException(string path, int id) + { + Path = path; + Id = id; + } + + public string Path { get; } + + public int Id { get; } + } +} diff --git a/src/installer/managed/Microsoft.NET.HostModel/ComHost/TypeLibraryDoesNotExistException.cs b/src/installer/managed/Microsoft.NET.HostModel/ComHost/TypeLibraryDoesNotExistException.cs new file mode 100644 index 00000000000000..28d20b547d4dd0 --- /dev/null +++ b/src/installer/managed/Microsoft.NET.HostModel/ComHost/TypeLibraryDoesNotExistException.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Microsoft.NET.HostModel.ComHost +{ + /// + /// The specified type library path does not exist. + /// + public class TypeLibraryDoesNotExistException : Exception + { + public TypeLibraryDoesNotExistException(string path, Exception innerException) + :base($"Type library '{path}' does not exist.", innerException) + { + Path = path; + } + + public string Path { get; } + } +} diff --git a/src/installer/managed/Microsoft.NET.HostModel/ResourceUpdater.cs b/src/installer/managed/Microsoft.NET.HostModel/ResourceUpdater.cs index e18eedf9804d28..bf1c0c489a93aa 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/ResourceUpdater.cs +++ b/src/installer/managed/Microsoft.NET.HostModel/ResourceUpdater.cs @@ -44,6 +44,16 @@ public static extern bool UpdateResource(SafeUpdateHandle hUpdate, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=5)] byte[] lpData, uint cbData); + // Update a resource with data from a managed byte[] + [DllImport(nameof(Kernel32), SetLastError=true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool UpdateResource(SafeUpdateHandle hUpdate, + string lpType, + IntPtr lpName, + ushort wLanguage, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=5)] byte[] lpData, + uint cbData); + [DllImport(nameof(Kernel32), SetLastError=true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool EndUpdateResource(SafeUpdateHandle hUpdate, @@ -277,7 +287,7 @@ public ResourceUpdater AddResourcesFromPEImage(string peFile) return this; } - private static bool IsIntResource(IntPtr lpType) + internal static bool IsIntResource(IntPtr lpType) { return ((uint)lpType >> 16) == 0; } @@ -308,6 +318,32 @@ public ResourceUpdater AddResource(byte[] data, IntPtr lpType, IntPtr lpName) return this; } + /// + /// Add a language-neutral integer resource from a byte[] with + /// a particular type and name. This will not modify the + /// target until Update() is called. + /// Throws an InvalidOperationException if Update() was already called. + /// + public ResourceUpdater AddResource(byte[] data, string lpType, IntPtr lpName) + { + if (hUpdate.IsInvalid) + { + ThrowExceptionForInvalidUpdate(); + } + + if (!IsIntResource(lpName)) + { + throw new ArgumentException("AddResource can only be used with integer resource names"); + } + + if (!Kernel32.UpdateResource(hUpdate, lpType, lpName, Kernel32.LangID_LangNeutral_SublangNeutral, data, (uint)data.Length)) + { + ThrowExceptionForLastWin32Error(); + } + + return this; + } + /// /// Write the pending resource updates to the target PE /// file. After this, the ResourceUpdater no longer maintains diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props index bc114a7ec5c1bb..b2c903cb38dd06 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props @@ -18,6 +18,9 @@ --> + + + diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.sfxproj b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.sfxproj index 153612f0e2bad8..6ab3f0743c9452 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.sfxproj +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.sfxproj @@ -27,7 +27,7 @@ The .NET Shared Framework - + Mono diff --git a/src/installer/tests/Assets/TestProjects/ComLibrary/ComLibrary.cs b/src/installer/tests/Assets/TestProjects/ComLibrary/ComLibrary.cs index d5eb1adadae24a..cf4418acd27448 100644 --- a/src/installer/tests/Assets/TestProjects/ComLibrary/ComLibrary.cs +++ b/src/installer/tests/Assets/TestProjects/ComLibrary/ComLibrary.cs @@ -12,10 +12,16 @@ public class UserDefinedAttribute : Attribute { } + [ComVisible(true)] + [Guid("27293cc8-7933-4fdf-9fde-653cbf9b55df")] + public interface IServer + { + } + [UserDefined] [ComVisible(true)] [Guid("438968CE-5950-4FBC-90B0-E64691350DF5")] - public class Server + public class Server : IServer { public Server() { @@ -28,6 +34,12 @@ public class NotComVisible { } + [ComVisible(true)] + [Guid("f7199267-9821-4f5b-924b-ab5246b455cd")] + public interface INested + { + } + [ComVisible(true)] [Guid("36e75747-aecd-43bf-9082-1a605889c762")] public class ComVisible @@ -35,7 +47,7 @@ public class ComVisible [UserDefined] [ComVisible(true)] [Guid("c82e4585-58bd-46e0-a76d-c0b6975e5984")] - public class Nested + public class Nested : INested { } } @@ -46,7 +58,7 @@ internal class ComVisibleNonPublic { [ComVisible(true)] [Guid("8a0a7085-aca4-4651-9878-ca42747e2206")] - public class Nested + public class Nested : INested { } } diff --git a/src/installer/tests/HostActivation.Tests/NativeHosting/Comhost.cs b/src/installer/tests/HostActivation.Tests/NativeHosting/Comhost.cs index 505b0c00c6c59f..4b693e371f1079 100644 --- a/src/installer/tests/HostActivation.Tests/NativeHosting/Comhost.cs +++ b/src/installer/tests/HostActivation.Tests/NativeHosting/Comhost.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; using System.IO; using System.Reflection.Metadata; using System.Runtime.InteropServices; @@ -119,6 +120,38 @@ public void ActivateClass_ValidateIErrorInfoResult() } } + [Fact] + public void LoadTypeLibraries() + { + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + // COM activation is only supported on Windows + return; + } + + using (var fixture = sharedState.ComLibraryFixture.Copy()) + { + var comHost = Path.Combine( + fixture.TestProject.BuiltApp.Location, + $"{ fixture.TestProject.AssemblyName }.comhost.dll"); + + string[] args = { + "comhost", + "typelib", + "2", + comHost, + sharedState.ClsidString + }; + CommandResult result = sharedState.CreateNativeHostCommand(args, fixture.BuiltDotnet.BinPath) + .Execute(); + + result.Should().Pass() + .And.HaveStdOutContaining("Loading default type library succeeded.") + .And.HaveStdOutContaining("Loading type library 1 succeeded.") + .And.HaveStdOutContaining("Loading type library 2 succeeded."); + } + } + public class SharedTestState : SharedTestStateBase { public string ComHostPath { get; } @@ -150,14 +183,23 @@ public SharedTestState() } } - // Use the locally built comhost to create a comhost with the embedded .clsidmap + // Use the locally built comhost to create a comhost with the embedded .clsidmap ComHostPath = Path.Combine( ComLibraryFixture.TestProject.BuiltApp.Location, $"{ ComLibraryFixture.TestProject.AssemblyName }.comhost.dll"); + + // Include the test type libraries in the ComHost tests. + var typeLibraries = new Dictionary + { + { 1, Path.Combine(RepoDirectories.Artifacts, "corehost_test", "Server.tlb") }, + { 2, Path.Combine(RepoDirectories.Artifacts, "corehost_test", "Nested.tlb") } + }; + ComHost.Create( Path.Combine(RepoDirectories.HostArtifacts, "comhost.dll"), ComHostPath, - clsidMapPath); + clsidMapPath, + typeLibraries); } protected override void Dispose(bool disposing) diff --git a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Ssl.ProtocolSupport.cs b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Ssl.ProtocolSupport.cs new file mode 100644 index 00000000000000..65981eccfb3aba --- /dev/null +++ b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Ssl.ProtocolSupport.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; +using System.Security.Authentication; + +internal static partial class Interop +{ + internal static partial class AndroidCrypto + { + [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLGetSupportedProtocols")] + internal static extern SslProtocols SSLGetSupportedProtocols(); + } +} diff --git a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Ssl.cs b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Ssl.cs index 133dc3ec445d13..fcd6cab6fcaf12 100644 --- a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Ssl.cs +++ b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Ssl.cs @@ -2,13 +2,144 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Buffers; using System.Runtime.InteropServices; +using System.Security.Authentication; +using System.Security.Cryptography.X509Certificates; + using Microsoft.Win32.SafeHandles; +using SafeSslHandle = System.Net.SafeSslHandle; + internal static partial class Interop { internal static partial class AndroidCrypto { + internal unsafe delegate PAL_SSLStreamStatus SSLReadCallback(byte* data, int* length); + internal unsafe delegate void SSLWriteCallback(byte* data, int length); + + internal enum PAL_SSLStreamStatus + { + OK = 0, + NeedData = 1, + Error = 2, + Renegotiate = 3, + Closed = 4, + }; + + [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamCreate")] + internal static extern SafeSslHandle SSLStreamCreate(); + + [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamCreateWithCertificates")] + private static extern SafeSslHandle SSLStreamCreateWithCertificates( + ref byte pkcs8PrivateKey, + int pkcs8PrivateKeyLen, + PAL_KeyAlgorithm algorithm, + IntPtr[] certs, + int certsLen); + internal static SafeSslHandle SSLStreamCreateWithCertificates(ReadOnlySpan pkcs8PrivateKey, PAL_KeyAlgorithm algorithm, IntPtr[] certificates) + { + return SSLStreamCreateWithCertificates( + ref MemoryMarshal.GetReference(pkcs8PrivateKey), + pkcs8PrivateKey.Length, + algorithm, + certificates, + certificates.Length); + } + + [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamInitialize")] + private static extern int SSLStreamInitializeImpl( + SafeSslHandle sslHandle, + [MarshalAs(UnmanagedType.U1)] bool isServer, + SSLReadCallback streamRead, + SSLWriteCallback streamWrite, + int appBufferSize); + internal static void SSLStreamInitialize( + SafeSslHandle sslHandle, + bool isServer, + SSLReadCallback streamRead, + SSLWriteCallback streamWrite, + int appBufferSize) + { + int ret = SSLStreamInitializeImpl(sslHandle, isServer, streamRead, streamWrite, appBufferSize); + if (ret != SUCCESS) + throw new SslException(); + } + + [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamConfigureParameters")] + private static extern int SSLStreamConfigureParametersImpl( + SafeSslHandle sslHandle, + [MarshalAs(UnmanagedType.LPUTF8Str)] string targetHost); + internal static void SSLStreamConfigureParameters( + SafeSslHandle sslHandle, + string targetHost) + { + int ret = SSLStreamConfigureParametersImpl(sslHandle, targetHost); + if (ret != SUCCESS) + throw new SslException(); + } + + [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamSetEnabledProtocols")] + private static extern int SSLStreamSetEnabledProtocols(SafeSslHandle sslHandle, ref SslProtocols protocols, int length); + internal static void SSLStreamSetEnabledProtocols(SafeSslHandle sslHandle, ReadOnlySpan protocols) + { + int ret = SSLStreamSetEnabledProtocols(sslHandle, ref MemoryMarshal.GetReference(protocols), protocols.Length); + if (ret != SUCCESS) + throw new SslException(); + } + + [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamHandshake")] + internal static extern PAL_SSLStreamStatus SSLStreamHandshake(SafeSslHandle sslHandle); + + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamGetApplicationProtocol")] + private static extern int SSLStreamGetApplicationProtocol(SafeSslHandle ssl, [Out] byte[]? buf, ref int len); + internal static byte[]? SSLStreamGetApplicationProtocol(SafeSslHandle ssl) + { + int len = 0; + int ret = SSLStreamGetApplicationProtocol(ssl, null, ref len); + if (ret != INSUFFICIENT_BUFFER) + return null; + + byte[] bytes = new byte[len]; + ret = SSLStreamGetApplicationProtocol(ssl, bytes, ref len); + if (ret != SUCCESS) + return null; + + return bytes; + } + + [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamRead")] + private static unsafe extern PAL_SSLStreamStatus SSLStreamRead( + SafeSslHandle sslHandle, + byte* buffer, + int length, + out int bytesRead); + internal static unsafe PAL_SSLStreamStatus SSLStreamRead( + SafeSslHandle sslHandle, + Span buffer, + out int bytesRead) + { + fixed (byte* bufferPtr = buffer) + { + return SSLStreamRead(sslHandle, bufferPtr, buffer.Length, out bytesRead); + } + } + + [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamWrite")] + private static unsafe extern PAL_SSLStreamStatus SSLStreamWrite( + SafeSslHandle sslHandle, + byte* buffer, + int length); + internal static unsafe PAL_SSLStreamStatus SSLStreamWrite( + SafeSslHandle sslHandle, + ReadOnlyMemory buffer) + { + using (MemoryHandle memHandle = buffer.Pin()) + { + return SSLStreamWrite(sslHandle, (byte*)memHandle.Pointer, buffer.Length); + } + } + [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamRelease")] internal static extern void SSLStreamRelease(IntPtr ptr); @@ -23,6 +154,78 @@ internal SslException(int errorCode) HResult = errorCode; } } + + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamGetProtocol")] + private static extern int SSLStreamGetProtocol(SafeSslHandle ssl, out IntPtr protocol); + internal static string SSLStreamGetProtocol(SafeSslHandle ssl) + { + IntPtr protocolPtr; + int ret = SSLStreamGetProtocol(ssl, out protocolPtr); + if (ret != SUCCESS) + throw new SslException(); + + if (protocolPtr == IntPtr.Zero) + return string.Empty; + + string protocol = Marshal.PtrToStringUni(protocolPtr)!; + Marshal.FreeHGlobal(protocolPtr); + return protocol; + } + + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamGetPeerCertificate")] + private static extern int SSLStreamGetPeerCertificate(SafeSslHandle ssl, out SafeX509Handle cert); + internal static SafeX509Handle SSLStreamGetPeerCertificate(SafeSslHandle ssl) + { + SafeX509Handle cert; + int ret = Interop.AndroidCrypto.SSLStreamGetPeerCertificate(ssl, out cert); + if (ret != SUCCESS) + throw new SslException(); + + return cert; + } + + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamGetPeerCertificates")] + private static extern int SSLStreamGetPeerCertificates( + SafeSslHandle ssl, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] out IntPtr[] certs, + out int count); + internal static IntPtr[] SSLStreamGetPeerCertificates(SafeSslHandle ssl) + { + IntPtr[] ptrs; + int count; + int ret = Interop.AndroidCrypto.SSLStreamGetPeerCertificates(ssl, out ptrs, out count); + if (ret != SUCCESS) + throw new SslException(); + + return ptrs; + } + + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamGetCipherSuite")] + private static extern int SSLStreamGetCipherSuite(SafeSslHandle ssl, out IntPtr cipherSuite); + internal static string SSLStreamGetCipherSuite(SafeSslHandle ssl) + { + IntPtr cipherSuitePtr; + int ret = SSLStreamGetCipherSuite(ssl, out cipherSuitePtr); + if (ret != SUCCESS) + throw new SslException(); + + if (cipherSuitePtr == IntPtr.Zero) + return string.Empty; + + string cipherSuite = Marshal.PtrToStringUni(cipherSuitePtr)!; + Marshal.FreeHGlobal(cipherSuitePtr); + return cipherSuite; + } + + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamShutdown")] + [return: MarshalAs(UnmanagedType.U1)] + internal static extern bool SSLStreamShutdown(SafeSslHandle ssl); + + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamVerifyHostname")] + [return: MarshalAs(UnmanagedType.U1)] + internal static extern bool SSLStreamVerifyHostname( + SafeSslHandle ssl, + [MarshalAs(UnmanagedType.LPUTF8Str)] string hostname); } } diff --git a/src/libraries/Common/src/Interop/OSX/System.Native/Interop.iOSSupportVersion.cs b/src/libraries/Common/src/Interop/OSX/System.Native/Interop.iOSSupportVersion.cs new file mode 100644 index 00000000000000..adffe6b4600791 --- /dev/null +++ b/src/libraries/Common/src/Interop/OSX/System.Native/Interop.iOSSupportVersion.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Sys + { + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_iOSSupportVersion")] + internal static extern string iOSSupportVersion(); + } +} diff --git a/src/libraries/Common/src/System/SR.cs b/src/libraries/Common/src/System/SR.cs index 6d116dd9a4fcb4..84da3bc46878a2 100644 --- a/src/libraries/Common/src/System/SR.cs +++ b/src/libraries/Common/src/System/SR.cs @@ -23,11 +23,11 @@ private static bool UsingResourceKeys() => false; #endif - internal static string GetResourceString(string resourceKey, string? defaultString = null) + internal static string GetResourceString(string resourceKey) { if (UsingResourceKeys()) { - return defaultString ?? resourceKey; + return resourceKey; } string? resourceString = null; @@ -42,14 +42,16 @@ internal static string GetResourceString(string resourceKey, string? defaultStri } catch (MissingManifestResourceException) { } - if (defaultString != null && resourceKey.Equals(resourceString)) - { - return defaultString; - } - return resourceString!; // only null if missing resources } + internal static string GetResourceString(string resourceKey, string defaultString) + { + string resourceString = GetResourceString(resourceKey); + + return resourceKey == resourceString || resourceString == null ? defaultString : resourceString; + } + internal static string Format(string resourceFormat, object? p1) { if (UsingResourceKeys()) diff --git a/src/libraries/Common/src/System/Text/ValueStringBuilder.cs b/src/libraries/Common/src/System/Text/ValueStringBuilder.cs index c4c4ea1ba478da..fbba96711347e1 100644 --- a/src/libraries/Common/src/System/Text/ValueStringBuilder.cs +++ b/src/libraries/Common/src/System/Text/ValueStringBuilder.cs @@ -158,7 +158,12 @@ public void Insert(int index, string? s) int remaining = _pos - index; _chars.Slice(index, remaining).CopyTo(_chars.Slice(index + count)); - s.AsSpan().CopyTo(_chars.Slice(index)); +#if SYSTEM_PRIVATE_CORELIB + s +#else + s.AsSpan() +#endif + .CopyTo(_chars.Slice(index)); _pos += count; } @@ -205,7 +210,12 @@ private void AppendSlow(string s) Grow(s.Length); } - s.AsSpan().CopyTo(_chars.Slice(pos)); +#if SYSTEM_PRIVATE_CORELIB + s +#else + s.AsSpan() +#endif + .CopyTo(_chars.Slice(pos)); _pos += s.Length; } diff --git a/src/libraries/Common/tests/StreamConformanceTests/System/IO/StreamConformanceTests.cs b/src/libraries/Common/tests/StreamConformanceTests/System/IO/StreamConformanceTests.cs index b6ba47288f47eb..edd51b148cf0ff 100644 --- a/src/libraries/Common/tests/StreamConformanceTests/System/IO/StreamConformanceTests.cs +++ b/src/libraries/Common/tests/StreamConformanceTests/System/IO/StreamConformanceTests.cs @@ -18,7 +18,7 @@ namespace System.IO.Tests { /// Base class providing tests for any Stream-derived type. - [PlatformSpecific(~TestPlatforms.Browser)] // lots of operations aren't supported on browser + [SkipOnPlatform(TestPlatforms.Browser, "lots of operations aren't supported on browser")] public abstract class StreamConformanceTests : FileCleanupTestBase { /// Gets the name of the byte[] argument to Read/Write methods. diff --git a/src/libraries/Common/tests/System/Net/Configuration.Certificates.cs b/src/libraries/Common/tests/System/Net/Configuration.Certificates.cs index 27f81002f5f1cb..d3b0cf224dae30 100644 --- a/src/libraries/Common/tests/System/Net/Configuration.Certificates.cs +++ b/src/libraries/Common/tests/System/Net/Configuration.Certificates.cs @@ -37,10 +37,9 @@ static Certificates() { try { - string contosoPfxSuffix = PlatformSupport.IsRC2Supported ? ".pfx" : ".noRC2.pfx"; - byte[] serverCertificateBytes = File.ReadAllBytes(Path.Combine(TestDataFolder, $"testservereku.contoso.com{contosoPfxSuffix}")); - byte[] clientCertificateBytes = File.ReadAllBytes(Path.Combine(TestDataFolder, $"testclienteku.contoso.com{contosoPfxSuffix}")); - byte[] noEKUCertificateBytes = File.ReadAllBytes(Path.Combine(TestDataFolder, $"testnoeku.contoso.com{contosoPfxSuffix}")); + byte[] serverCertificateBytes = File.ReadAllBytes(Path.Combine(TestDataFolder, "testservereku.contoso.com.pfx")); + byte[] clientCertificateBytes = File.ReadAllBytes(Path.Combine(TestDataFolder, "testclienteku.contoso.com.pfx")); + byte[] noEKUCertificateBytes = File.ReadAllBytes(Path.Combine(TestDataFolder, "testnoeku.contoso.com.pfx")); byte[] selfSignedServerCertificateBytes = File.ReadAllBytes(Path.Combine(TestDataFolder, "testselfsignedservereku.contoso.com.pfx")); byte[] selfSignedClientCertificateBytes = File.ReadAllBytes(Path.Combine(TestDataFolder, "testselfsignedclienteku.contoso.com.pfx")); @@ -64,7 +63,7 @@ static Certificates() } } } - + // These Get* methods make a copy of the certificates so that consumers own the lifetime of the // certificates handed back. Consumers are expected to dispose of their certs when done with them. diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Proxy.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Proxy.cs index 208ac1ad2916ca..c4beb328d6ea8d 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Proxy.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Proxy.cs @@ -320,21 +320,129 @@ public async Task Proxy_SslProxyUnsupported_Throws() } } - [OuterLoop("Uses external server")] [Fact] - public async Task Proxy_SendSecureRequestThruProxy_ConnectTunnelUsed() + public async Task ProxyTunnelRequest_GetAsync_Success() { + if (IsWinHttpHandler) + { + return; + } + + const string Content = "Hello world"; + using (LoopbackProxyServer proxyServer = LoopbackProxyServer.Create()) { HttpClientHandler handler = CreateHttpClientHandler(); handler.Proxy = new WebProxy(proxyServer.Uri); + handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates; using (HttpClient client = CreateHttpClient(handler)) - using (HttpResponseMessage response = await client.GetAsync(Configuration.Http.SecureRemoteEchoServer)) { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - _output.WriteLine($"Proxy request line: {proxyServer.Requests[0].RequestLine}"); - Assert.Contains("CONNECT", proxyServer.Requests[0].RequestLine); + var options = new LoopbackServer.Options { UseSsl = true }; + await LoopbackServer.CreateServerAsync(async (server, uri) => + { + Assert.Equal(proxyServer.Uri, handler.Proxy.GetProxy(uri)); + + Task clientTask = client.GetAsync(uri); + await server.AcceptConnectionSendResponseAndCloseAsync(content: Content); + using (var response = await clientTask) + { + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(Content, await response.Content.ReadAsStringAsync()); + } + }, options); + } + + Assert.Contains("CONNECT", proxyServer.Requests[0].RequestLine); + } + } + + [Fact] + public async Task ProxyTunnelRequest_MaxConnectionsSetButDoesNotApplyToProxyConnect_Success() + { + if (IsWinHttpHandler) + { + return; + } + + const string Content = "Hello world"; + + using (LoopbackProxyServer proxyServer = LoopbackProxyServer.Create()) + { + HttpClientHandler handler = CreateHttpClientHandler(); + handler.Proxy = new WebProxy(proxyServer.Uri); + handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates; + handler.MaxConnectionsPerServer = 1; + using (HttpClient client = CreateHttpClient(handler)) + { + var options = new LoopbackServer.Options { UseSsl = true }; + await LoopbackServer.CreateServerAsync(async (server1, uri1) => + { + await LoopbackServer.CreateServerAsync(async (server2, uri2) => + { + Assert.Equal(proxyServer.Uri, handler.Proxy.GetProxy(uri1)); + Assert.Equal(proxyServer.Uri, handler.Proxy.GetProxy(uri2)); + + Task clientTask1 = client.GetAsync(uri1); + Task clientTask2 = client.GetAsync(uri2); + await server1.AcceptConnectionAsync(async connection1 => + { + await server2.AcceptConnectionAsync(async connection2 => + { + await connection1.HandleRequestAsync(content: Content); + await connection2.HandleRequestAsync(content: Content); + }); + }); + + using (var response1 = await clientTask1) + { + Assert.Equal(HttpStatusCode.OK, response1.StatusCode); + Assert.Equal(Content, await response1.Content.ReadAsStringAsync()); + } + + using (var response2 = await clientTask2) + { + Assert.Equal(HttpStatusCode.OK, response2.StatusCode); + Assert.Equal(Content, await response2.Content.ReadAsStringAsync()); + } + }, options); + }, options); + } + + Assert.Contains("CONNECT", proxyServer.Requests[0].RequestLine); + Assert.Contains("CONNECT", proxyServer.Requests[1].RequestLine); + } + } + + [Fact] + public async Task ProxyTunnelRequest_OriginServerSendsProxyAuthChallenge_NoProxyAuthPerformed() + { + if (IsWinHttpHandler) + { + return; + } + + using (LoopbackProxyServer proxyServer = LoopbackProxyServer.Create()) + { + HttpClientHandler handler = CreateHttpClientHandler(); + handler.Proxy = new WebProxy(proxyServer.Uri) { Credentials = ConstructCredentials(new NetworkCredential("username", "password"), proxyServer.Uri, BasicAuth, true) }; + handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates; + using (HttpClient client = CreateHttpClient(handler)) + { + var options = new LoopbackServer.Options { UseSsl = true }; + await LoopbackServer.CreateServerAsync(async (server, uri) => + { + Assert.Equal(proxyServer.Uri, handler.Proxy.GetProxy(uri)); + + Task clientTask = client.GetAsync(uri); + await server.AcceptConnectionSendResponseAndCloseAsync(statusCode: HttpStatusCode.ProxyAuthenticationRequired, additionalHeaders: "Proxy-Authenticate: Basic"); + using (var response = await clientTask) + { + Assert.Equal(HttpStatusCode.ProxyAuthenticationRequired, response.StatusCode); + } + }, options); } + + Assert.Contains("CONNECT", proxyServer.Requests[0].RequestLine); } } @@ -373,7 +481,6 @@ await LoopbackServer.CreateServerAsync(async (proxyServer, proxyUrl) => Assert.Equal(HttpStatusCode.OK, clientTask.Result.StatusCode); } }, options); - } [Fact] diff --git a/src/libraries/Common/tests/System/Net/Http/LoopbackProxyServer.cs b/src/libraries/Common/tests/System/Net/Http/LoopbackProxyServer.cs index 2429e0302d9ff9..0f4999b703497d 100644 --- a/src/libraries/Common/tests/System/Net/Http/LoopbackProxyServer.cs +++ b/src/libraries/Common/tests/System/Net/Http/LoopbackProxyServer.cs @@ -143,7 +143,12 @@ private async Task ProcessRequest(Socket clientSocket, StreamReader reader } var request = new ReceivedRequest(); - _requests.Add(request); + + // Avoid concurrent writes to the request list + lock (_requests) + { + _requests.Add(request); + } request.RequestLine = line; string[] requestTokens = request.RequestLine.Split(' '); diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherTests.cs index d3fbeb7628d015..e1f5da5f40f9ef 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherTests.cs @@ -11,7 +11,7 @@ namespace System.Security.Cryptography.Encryption.Aes.Tests { using Aes = System.Security.Cryptography.Aes; - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public partial class AesCipherTests { [Fact] diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesContractTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesContractTests.cs index 8c0c79da25cc28..89e3ebbe61f3a8 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesContractTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesContractTests.cs @@ -7,7 +7,7 @@ namespace System.Security.Cryptography.Encryption.Aes.Tests { using Aes = System.Security.Cryptography.Aes; - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public class AesContractTests { [Fact] diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCornerTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCornerTests.cs index f98b8a9ce688df..9716824ba2bbc9 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCornerTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCornerTests.cs @@ -12,7 +12,7 @@ namespace System.Security.Cryptography.Encryption.Aes.Tests { using Aes = System.Security.Cryptography.Aes; - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public static class AesCornerTests { [Fact] diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesModeTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesModeTests.cs index 6be8a5f9ef57fc..1a496a505c6660 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesModeTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesModeTests.cs @@ -7,7 +7,7 @@ namespace System.Security.Cryptography.Encryption.Aes.Tests { using Aes = System.Security.Cryptography.Aes; - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public class AesModeTests { [Fact] diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/DecryptorReusability.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/DecryptorReusability.cs index 76d1bc5359bb57..99e03904aef915 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/DecryptorReusability.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/DecryptorReusability.cs @@ -7,7 +7,7 @@ namespace System.Security.Cryptography.Encryption.Aes.Tests { using Aes = System.Security.Cryptography.Aes; - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public static class DecryptorReusabilty { // See https://github.com/dotnet/runtime/issues/21354 for details diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESCipherTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESCipherTests.cs index 82b076821f4691..d007a41167e180 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESCipherTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESCipherTests.cs @@ -9,7 +9,7 @@ namespace System.Security.Cryptography.Encryption.Des.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public static class DesCipherTests { // These are the expected output of many decryptions. Changing these values requires re-generating test input. diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESContractTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESContractTests.cs index 34eed4dad5850d..145351ac24a2dd 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESContractTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESContractTests.cs @@ -9,7 +9,7 @@ namespace System.Security.Cryptography.Encryption.Des.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public static class DesContractTests { // cfb not available on windows 7 diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DesTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DesTests.cs index b906485fc4d723..71f41c685a8117 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DesTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DesTests.cs @@ -7,7 +7,7 @@ namespace System.Security.Cryptography.Encryption.Des.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public static partial class DesTests { private static readonly byte[] KnownWeakKey = "e0e0e0e0f1f1f1f1".HexToByteArray(); diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAImportExport.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAImportExport.cs index fc75cc836957d4..6962ab68e6cecd 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAImportExport.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAImportExport.cs @@ -5,7 +5,7 @@ namespace System.Security.Cryptography.Dsa.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public partial class DSAImportExport { public static bool SupportsFips186_3 => DSAFactory.SupportsFips186_3; diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyFileTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyFileTests.cs index 885c9a075261fd..90a5d0b1c70915 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyFileTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyFileTests.cs @@ -8,7 +8,7 @@ namespace System.Security.Cryptography.Dsa.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public static class DSAKeyFileTests { public static bool SupportsFips186_3 => DSAFactory.SupportsFips186_3; diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyGeneration.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyGeneration.cs index 3c2b5974684f59..3d1d3ea6d476ca 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyGeneration.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyGeneration.cs @@ -5,7 +5,7 @@ namespace System.Security.Cryptography.Dsa.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public partial class DSAKeyGeneration { public static bool SupportsKeyGeneration => DSAFactory.SupportsKeyGeneration; diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyPemTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyPemTests.cs index 199135a4b58f33..b862e26a122fd5 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyPemTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyPemTests.cs @@ -7,7 +7,7 @@ namespace System.Security.Cryptography.Dsa.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public static class DSAKeyPemTests { private const string AmbiguousExceptionMarker = "multiple keys"; diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignVerify.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignVerify.cs index b1a8c9e48fa26a..0354fa28f2d100 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignVerify.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignVerify.cs @@ -7,7 +7,7 @@ namespace System.Security.Cryptography.Dsa.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public sealed class DSASignVerify_Array : DSASignVerify { public override byte[] SignData(DSA dsa, byte[] data, HashAlgorithmName hashAlgorithm) => @@ -54,7 +54,7 @@ public void InvalidStreamArrayArguments_Throws() } } - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public sealed class DSASignVerify_Stream : DSASignVerify { public override byte[] SignData(DSA dsa, byte[] data, HashAlgorithmName hashAlgorithm) => @@ -76,7 +76,7 @@ public void InvalidArrayArguments_Throws() } #if NETCOREAPP - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public sealed class DSASignVerify_Span : DSASignVerify { public override byte[] SignData(DSA dsa, byte[] data, HashAlgorithmName hashAlgorithm) => @@ -109,7 +109,7 @@ private static byte[] TryWithOutputArray(Func func) } } #endif - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public abstract partial class DSASignVerify { public abstract byte[] SignData(DSA dsa, byte[] data, HashAlgorithmName hashAlgorithm); diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignatureFormatTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignatureFormatTests.cs index 6b9eab6279f105..764254cd31c9ec 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignatureFormatTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignatureFormatTests.cs @@ -8,7 +8,7 @@ namespace System.Security.Cryptography.Dsa.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public abstract class DSASignatureFormatTests : DsaFamilySignatureFormatTests { protected override bool SupportsSha2 => DSAFactory.SupportsFips186_3; diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignatureFormatter.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignatureFormatter.cs index 22c3a02648a576..3aea7c0bcf2db3 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignatureFormatter.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignatureFormatter.cs @@ -6,7 +6,7 @@ namespace System.Security.Cryptography.Dsa.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public partial class DSASignatureFormatterTests : AsymmetricSignatureFormatterTests { [Fact] diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAXml.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAXml.cs index 1a742d38f6069c..6f19dd555adcbe 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAXml.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAXml.cs @@ -7,7 +7,7 @@ namespace System.Security.Cryptography.Dsa.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public static class DSAXml { [Fact] diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DsaFamilySignatureFormatTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DsaFamilySignatureFormatTests.cs index d55b5a794bebdc..fde2f717f0be3f 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DsaFamilySignatureFormatTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DsaFamilySignatureFormatTests.cs @@ -8,7 +8,7 @@ namespace System.Security.Cryptography.Algorithms.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public abstract class DsaFamilySignatureFormatTests { protected readonly struct KeyDescription diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyFileTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyFileTests.cs index 66b80c93380e9b..407ad99a41e3d1 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyFileTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyFileTests.cs @@ -8,7 +8,7 @@ namespace System.Security.Cryptography.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public abstract partial class ECKeyFileTests where T : AsymmetricAlgorithm { protected abstract T CreateKey(); diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyPemTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyPemTests.cs index 1593a5df2f4b09..be7670c006da64 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyPemTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyPemTests.cs @@ -7,7 +7,7 @@ namespace System.Security.Cryptography.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public abstract class ECKeyPemTests where TAlg : AsymmetricAlgorithm { private const string AmbiguousExceptionMarker = "multiple keys"; diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDhKeyFileTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDhKeyFileTests.cs index 6fb60fb9596122..2b814f581f90d5 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDhKeyFileTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDhKeyFileTests.cs @@ -6,7 +6,7 @@ namespace System.Security.Cryptography.EcDiffieHellman.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public class ECDhKeyFileTests : ECKeyFileTests { protected override ECDiffieHellman CreateKey() diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanKeyPemTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanKeyPemTests.cs index 78bc9a300a4e8c..a03cdb0c766d0e 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanKeyPemTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanKeyPemTests.cs @@ -6,7 +6,7 @@ namespace System.Security.Cryptography.EcDsa.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public sealed class ECDiffieHellmanKeyPemTests : ECKeyPemTests { protected override ECDiffieHellman CreateKey() => ECDiffieHellman.Create(); diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanTests.ImportExport.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanTests.ImportExport.cs index 4992fc7ef86cd6..7ee472cf247f2e 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanTests.ImportExport.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanTests.ImportExport.cs @@ -154,7 +154,7 @@ public static void TestKeySizeCreateKey() } [Fact] - [PlatformSpecific(~TestPlatforms.Android)] // Android does not validate curve parameters + [SkipOnPlatform(TestPlatforms.Android, "Android does not validate curve parameters")] public static void TestExplicitImportValidationNegative() { if (!ECDiffieHellmanFactory.ExplicitCurvesSupported) diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanTests.cs index 920b5b25a3a304..412b48e47502aa 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanTests.cs @@ -9,7 +9,7 @@ namespace System.Security.Cryptography.EcDiffieHellman.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public partial class ECDiffieHellmanTests : EccTestBase { private static List s_everyKeysize; diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaImportExport.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaImportExport.cs index 777b217be37b10..adb9732c1477a0 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaImportExport.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaImportExport.cs @@ -7,7 +7,7 @@ namespace System.Security.Cryptography.EcDsa.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public class ECDsaImportExportTests : ECDsaTestsBase { internal static bool CanDeriveNewPublicKey { get; } @@ -188,7 +188,7 @@ public static void TestKeySizeCreateKey() } [ConditionalFact(nameof(ECExplicitCurvesSupported))] - [PlatformSpecific(~TestPlatforms.Android)] // Android does not validate curve parameters + [SkipOnPlatform(TestPlatforms.Android, "Android does not validate curve parameters")] public static void TestExplicitImportValidationNegative() { unchecked diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaKeyFileTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaKeyFileTests.cs index 61419f5f658102..46f88abb735a0d 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaKeyFileTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaKeyFileTests.cs @@ -6,7 +6,7 @@ namespace System.Security.Cryptography.EcDsa.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public class ECDsaKeyFileTests : ECKeyFileTests { protected override ECDsa CreateKey() diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaKeyPemTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaKeyPemTests.cs index a4e4e25763f077..b969afa0fe4550 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaKeyPemTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaKeyPemTests.cs @@ -6,7 +6,7 @@ namespace System.Security.Cryptography.EcDsa.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public sealed class ECDsaKeyPemTests : ECKeyPemTests { protected override ECDsa CreateKey() => ECDsa.Create(); diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaSignatureFormatTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaSignatureFormatTests.cs index 9d55ebc1bbe06a..c783d71be18812 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaSignatureFormatTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaSignatureFormatTests.cs @@ -9,7 +9,7 @@ namespace System.Security.Cryptography.EcDsa.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public abstract class ECDsaSignatureFormatTests : DsaFamilySignatureFormatTests { protected override bool SupportsSha2 => true; diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaTests.cs index 530027e87466b6..d747aec14e5517 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaTests.cs @@ -11,7 +11,7 @@ namespace System.Security.Cryptography.EcDsa.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public sealed class ECDsaTests_Array : ECDsaTests { protected override bool VerifyData(ECDsa ecdsa, byte[] data, int offset, int count, byte[] signature, HashAlgorithmName hashAlgorithm) => @@ -87,7 +87,7 @@ public void VerifyHash_InvalidArguments_Throws(ECDsa ecdsa) } } - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public sealed class ECDsaTests_Stream : ECDsaTests { protected override bool VerifyData(ECDsa ecdsa, byte[] data, int offset, int count, byte[] signature, HashAlgorithmName hashAlgorithm) @@ -125,7 +125,7 @@ public void VerifyData_InvalidArguments_Throws(ECDsa ecdsa) } } - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public abstract partial class ECDsaTests : ECDsaTestsBase { protected bool VerifyData(ECDsa ecdsa, byte[] data, byte[] signature, HashAlgorithmName hashAlgorithm) => diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaTests.netcoreapp.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaTests.netcoreapp.cs index 391bfaec06ad62..ab36bacd98d4eb 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaTests.netcoreapp.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaTests.netcoreapp.cs @@ -6,7 +6,7 @@ namespace System.Security.Cryptography.EcDsa.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public sealed class ECDsaTests_Span : ECDsaTests { protected override bool VerifyData(ECDsa ecdsa, byte[] data, int offset, int count, byte[] signature, HashAlgorithmName hashAlgorithm) => diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaTestsBase.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaTestsBase.cs index af03c8cecb7fcd..9da985adfd21fb 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaTestsBase.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaTestsBase.cs @@ -12,7 +12,7 @@ namespace System.Security.Cryptography.EcDsa.Tests /// /// Input and helper methods for ECDsa /// - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public abstract class ECDsaTestsBase : EccTestBase { #if NETCOREAPP diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaXml.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaXml.cs index dc8ab6a14d4fe4..6ac3ed634e5683 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaXml.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaXml.cs @@ -5,7 +5,7 @@ namespace System.Security.Cryptography.EcDsa.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public partial class ECDsaXml : ECDsaTestsBase { [Fact] diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2CipherTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2CipherTests.cs index 35fa8c9ecae6ad..7823cc8b1c58c9 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2CipherTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2CipherTests.cs @@ -10,7 +10,7 @@ namespace System.Security.Cryptography.Encryption.RC2.Tests { using RC2 = System.Security.Cryptography.RC2; - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] [ConditionalClass(typeof(RC2Factory), nameof(RC2Factory.IsSupported))] public static class RC2CipherTests { diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2ContractTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2ContractTests.cs index 85000abda615e9..91460058bd21bb 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2ContractTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2ContractTests.cs @@ -11,7 +11,7 @@ namespace System.Security.Cryptography.Encryption.RC2.Tests { using RC2 = System.Security.Cryptography.RC2; - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public static class RC2ContractTests { [Theory] diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2Tests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2Tests.cs index 98573f6a822394..b66501a2088614 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2Tests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2Tests.cs @@ -7,7 +7,7 @@ namespace System.Security.Cryptography.Encryption.RC2.Tests { using RC2 = System.Security.Cryptography.RC2; - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public static partial class RC2Tests { [Fact] diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/EncryptDecrypt.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/EncryptDecrypt.cs index 5cb0d6408f2e5d..96d9b51372847f 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/EncryptDecrypt.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/EncryptDecrypt.cs @@ -24,7 +24,7 @@ public void NullArray_Throws() } } - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public abstract class EncryptDecrypt { public static bool SupportsSha2Oaep => RSAFactory.SupportsSha2Oaep; diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/EncryptDecrypt.netcoreapp.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/EncryptDecrypt.netcoreapp.cs index cf5a46e9d8dec0..d4055512834477 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/EncryptDecrypt.netcoreapp.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/EncryptDecrypt.netcoreapp.cs @@ -6,7 +6,7 @@ namespace System.Security.Cryptography.Rsa.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public sealed class EncryptDecrypt_Span : EncryptDecrypt { protected override byte[] Encrypt(RSA rsa, byte[] data, RSAEncryptionPadding padding) => diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/ImportExport.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/ImportExport.cs index 7f2b093d302f16..ee608a62a30750 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/ImportExport.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/ImportExport.cs @@ -7,7 +7,7 @@ namespace System.Security.Cryptography.Rsa.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public partial class ImportExport { public static bool Supports16384 { get; } = TestRsa16384(); diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/KeyGeneration.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/KeyGeneration.cs index bc559c392a134e..ea13e350b977f9 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/KeyGeneration.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/KeyGeneration.cs @@ -5,7 +5,7 @@ namespace System.Security.Cryptography.Rsa.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public class KeyGeneration { [Fact] diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyExchangeFormatter.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyExchangeFormatter.cs index 0f95d2621b54e1..3e9071c25c8bea 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyExchangeFormatter.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyExchangeFormatter.cs @@ -7,7 +7,7 @@ namespace System.Security.Cryptography.Rsa.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public partial class RSAKeyExchangeFormatterTests { [Fact] diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyFileTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyFileTests.cs index 92d5b21e10433e..f3f9b1b85e8c04 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyFileTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyFileTests.cs @@ -8,7 +8,7 @@ namespace System.Security.Cryptography.Rsa.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public static class RSAKeyFileTests { public static bool Supports384BitPrivateKeyAndRC2 { get; } = RSAFactory.Supports384PrivateKey && RC2Factory.IsSupported; diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyPemTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyPemTests.cs index c76ab44a8d3226..806e996cf3a7c1 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyPemTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyPemTests.cs @@ -7,7 +7,7 @@ namespace System.Security.Cryptography.Rsa.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public static class RSAKeyPemTests { private const string AmbiguousExceptionMarker = "multiple keys"; diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSASignatureFormatter.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSASignatureFormatter.cs index d93f794ef1e47d..d58ac86a4cbebe 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSASignatureFormatter.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSASignatureFormatter.cs @@ -7,7 +7,7 @@ namespace System.Security.Cryptography.Rsa.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public partial class RSASignatureFormatterTests : AsymmetricSignatureFormatterTests { [Fact] diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAXml.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAXml.cs index a0ef210d47e80b..0427e190772b54 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAXml.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAXml.cs @@ -7,7 +7,7 @@ namespace System.Security.Cryptography.Rsa.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public static class RSAXml { [Fact] diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/SignVerify.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/SignVerify.cs index afeddd9be65de6..4a7dace2695205 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/SignVerify.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/SignVerify.cs @@ -9,7 +9,7 @@ namespace System.Security.Cryptography.Rsa.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public sealed class SignVerify_Array : SignVerify { protected override byte[] SignData(RSA rsa, byte[] data, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) => @@ -38,7 +38,7 @@ public void NullArray_Throws() } } - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public abstract class SignVerify { public static bool SupportsPss => RSAFactory.SupportsPss; diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESCipherTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESCipherTests.cs index e46e8a2514f03b..ce92a62f029b4f 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESCipherTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESCipherTests.cs @@ -8,7 +8,7 @@ namespace System.Security.Cryptography.Encryption.TripleDes.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public static class TripleDESCipherTests { [Fact] diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESContractTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESContractTests.cs index 43c6de567bcb16..e12aa7196c8473 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESContractTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESContractTests.cs @@ -7,7 +7,7 @@ namespace System.Security.Cryptography.Encryption.TripleDes.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public static class TripleDESContractTests { diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESReusabilityTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESReusabilityTests.cs index cb14fb90859853..4360b70332218d 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESReusabilityTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESReusabilityTests.cs @@ -6,7 +6,7 @@ namespace System.Security.Cryptography.Encryption.TripleDes.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public static class TripleDESReusabilityTests { [Fact] diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.Unix.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.Unix.cs index c9603889017322..73dff40f517dce 100644 --- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.Unix.cs +++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.Unix.cs @@ -52,7 +52,7 @@ public static partial class PlatformDetection public static bool IsSuperUser => IsBrowser || IsWindows ? false : libc.geteuid() == 0; - public static Version OpenSslVersion => !IsOSXLike && !IsWindows ? + public static Version OpenSslVersion => !IsOSXLike && !IsWindows && !IsAndroid ? GetOpenSslVersion() : throw new PlatformNotSupportedException(); diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs index 75879136f3291a..57b2df23622ce2 100644 --- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs +++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs @@ -85,7 +85,7 @@ public static bool IsDrawingSupported } } - + public static bool IsLineNumbersSupported => true; public static bool IsInContainer => GetIsInContainer(); @@ -293,6 +293,13 @@ private static bool OpenSslGetTlsSupport(SslProtocols protocol) return ret == 1; } + private static readonly Lazy s_androidSupportedSslProtocols = new Lazy(Interop.AndroidCrypto.SSLGetSupportedProtocols); + private static bool AndroidGetSslProtocolSupport(SslProtocols protocol) + { + Debug.Assert(IsAndroid); + return (protocol & s_androidSupportedSslProtocols.Value) == protocol; + } + private static bool GetTls10Support() { // on Windows, macOS, and Android TLS1.0/1.1 are supported. @@ -359,6 +366,14 @@ private static bool GetTls13Support() // [ActiveIssue("https://github.com/dotnet/runtime/issues/1979")] return false; } + else if (IsAndroid) + { +#if NETFRAMEWORK + return false; +#else + return AndroidGetSslProtocolSupport(SslProtocols.Tls13); +#endif + } else if (IsOpenSslSupported) { // Covers Linux, FreeBSD, illumos and Solaris diff --git a/src/libraries/Common/tests/TestUtilities/TestUtilities.csproj b/src/libraries/Common/tests/TestUtilities/TestUtilities.csproj index 3e6464239d718c..f0b1b772e27771 100644 --- a/src/libraries/Common/tests/TestUtilities/TestUtilities.csproj +++ b/src/libraries/Common/tests/TestUtilities/TestUtilities.csproj @@ -28,7 +28,7 @@ - @@ -66,6 +66,11 @@ + + + + diff --git a/src/libraries/Common/tests/Tests/System/Security/IdentityHelperTests.cs b/src/libraries/Common/tests/Tests/System/Security/IdentityHelperTests.cs index 1847a2e17ca5e3..a482693370dba3 100644 --- a/src/libraries/Common/tests/Tests/System/Security/IdentityHelperTests.cs +++ b/src/libraries/Common/tests/Tests/System/Security/IdentityHelperTests.cs @@ -28,7 +28,7 @@ public void ToBase32StringSuitableForDirName(byte[] buff, string expected) } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public void GetNormalizedStrongNameHash() { // Validating that we match the exact hash the .NET Framework IsolatedStorage implementation would create. @@ -36,7 +36,7 @@ public void GetNormalizedStrongNameHash() } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public void GetNormalizedUrlHash() { // Validating that we match the exact hash the .NET Framework IsolatedStorage implementation would create. diff --git a/src/libraries/Microsoft.CSharp/ref/Microsoft.CSharp.cs b/src/libraries/Microsoft.CSharp/ref/Microsoft.CSharp.cs index 720a654cdd5574..38ddafe717632e 100644 --- a/src/libraries/Microsoft.CSharp/ref/Microsoft.CSharp.cs +++ b/src/libraries/Microsoft.CSharp/ref/Microsoft.CSharp.cs @@ -9,16 +9,27 @@ namespace Microsoft.CSharp.RuntimeBinder [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public static partial class Binder { + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Using dynamic types might cause types or members to be removed by trimmer.")] public static System.Runtime.CompilerServices.CallSiteBinder BinaryOperation(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags flags, System.Linq.Expressions.ExpressionType operation, System.Type? context, System.Collections.Generic.IEnumerable? argumentInfo) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Using dynamic types might cause types or members to be removed by trimmer.")] public static System.Runtime.CompilerServices.CallSiteBinder Convert(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags flags, System.Type type, System.Type? context) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Using dynamic types might cause types or members to be removed by trimmer.")] public static System.Runtime.CompilerServices.CallSiteBinder GetIndex(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags flags, System.Type? context, System.Collections.Generic.IEnumerable? argumentInfo) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Using dynamic types might cause types or members to be removed by trimmer.")] public static System.Runtime.CompilerServices.CallSiteBinder GetMember(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags flags, string name, System.Type? context, System.Collections.Generic.IEnumerable? argumentInfo) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Using dynamic types might cause types or members to be removed by trimmer.")] public static System.Runtime.CompilerServices.CallSiteBinder Invoke(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags flags, System.Type? context, System.Collections.Generic.IEnumerable? argumentInfo) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Using dynamic types might cause types or members to be removed by trimmer.")] public static System.Runtime.CompilerServices.CallSiteBinder InvokeConstructor(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags flags, System.Type? context, System.Collections.Generic.IEnumerable? argumentInfo) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Using dynamic types might cause types or members to be removed by trimmer.")] public static System.Runtime.CompilerServices.CallSiteBinder InvokeMember(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags flags, string name, System.Collections.Generic.IEnumerable? typeArguments, System.Type? context, System.Collections.Generic.IEnumerable? argumentInfo) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Using dynamic types might cause types or members to be removed by trimmer.")] public static System.Runtime.CompilerServices.CallSiteBinder IsEvent(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags flags, string name, System.Type? context) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Using dynamic types might cause types or members to be removed by trimmer.")] public static System.Runtime.CompilerServices.CallSiteBinder SetIndex(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags flags, System.Type? context, System.Collections.Generic.IEnumerable? argumentInfo) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Using dynamic types might cause types or members to be removed by trimmer.")] public static System.Runtime.CompilerServices.CallSiteBinder SetMember(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags flags, string name, System.Type? context, System.Collections.Generic.IEnumerable? argumentInfo) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Using dynamic types might cause types or members to be removed by trimmer.")] public static System.Runtime.CompilerServices.CallSiteBinder UnaryOperation(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags flags, System.Linq.Expressions.ExpressionType operation, System.Type? context, System.Collections.Generic.IEnumerable? argumentInfo) { throw null; } } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] diff --git a/src/libraries/Microsoft.CSharp/src/ILLink/ILLink.Suppressions.xml b/src/libraries/Microsoft.CSharp/src/ILLink/ILLink.Suppressions.xml deleted file mode 100644 index 5c72e2df533ef7..00000000000000 --- a/src/libraries/Microsoft.CSharp/src/ILLink/ILLink.Suppressions.xml +++ /dev/null @@ -1,167 +0,0 @@ - - - - - ILLink - IL2026 - member - M:Microsoft.CSharp.RuntimeBinder.DynamicMetaObjectProviderDebugView.#cctor - - - ILLink - IL2055 - member - M:Microsoft.CSharp.RuntimeBinder.ComInterop.VariantArray.GetStructType(System.Int32) - - - ILLink - IL2055 - member - M:Microsoft.CSharp.RuntimeBinder.Semantics.AggregateType.CalculateAssociatedSystemType - - - ILLink - IL2060 - member - M:Microsoft.CSharp.RuntimeBinder.Semantics.ExprMethodInfo.get_MethodInfo - - - ILLink - IL2070 - member - M:Microsoft.CSharp.RuntimeBinder.RuntimeBinderExtensions.GetTypeIndexerName(System.Type) - - - ILLink - IL2070 - member - M:Microsoft.CSharp.RuntimeBinder.SymbolTable.AddAggregateToSymbolTable(Microsoft.CSharp.RuntimeBinder.Semantics.NamespaceOrAggregateSymbol,System.Type) - - - ILLink - IL2070 - member - M:Microsoft.CSharp.RuntimeBinder.SymbolTable.AddConversionsForOneType(System.Type) - - - ILLink - IL2072 - member - M:Microsoft.CSharp.RuntimeBinder.ComInterop.ComTypeClassDesc.CreateInstance - - - ILLink - IL2072 - member - M:Microsoft.CSharp.RuntimeBinder.Semantics.ExprFactory.CreateZeroInit(Microsoft.CSharp.RuntimeBinder.Semantics.CType) - - - ILLink - IL2072 - member - M:Microsoft.CSharp.RuntimeBinder.Semantics.ExprZeroInit.get_Object - - - ILLink - IL2075 - member - M:Microsoft.CSharp.RuntimeBinder.ComInterop.ComBinderHelpers.ProcessArgumentsForCom(System.Dynamic.DynamicMetaObject[]@) - - - ILLink - IL2075 - member - M:Microsoft.CSharp.RuntimeBinder.ComInterop.ExcepInfo.GetException - - - ILLink - IL2075 - member - M:Microsoft.CSharp.RuntimeBinder.ComInterop.IDispatchComObject.GetMembers(System.Collections.Generic.IEnumerable{System.String}) - - - ILLink - IL2075 - member - M:Microsoft.CSharp.RuntimeBinder.ComInterop.TypeUtils.GetUserDefinedCoercionMethod(System.Type,System.Type,System.Boolean) - - - ILLink - IL2075 - member - M:Microsoft.CSharp.RuntimeBinder.DynamicMetaObjectProviderDebugView.CreateDelegateAndInvoke(System.Type[],System.Runtime.CompilerServices.CallSiteBinder,System.Object[]) - - - ILLink - IL2075 - member - M:Microsoft.CSharp.RuntimeBinder.ExpressionTreeCallRewriter.GenerateField(Microsoft.CSharp.RuntimeBinder.Semantics.ExprCall) - - - ILLink - IL2075 - member - M:Microsoft.CSharp.RuntimeBinder.Semantics.ExprMethodInfo.get_ConstructorInfo - - - ILLink - IL2075 - member - M:Microsoft.CSharp.RuntimeBinder.Semantics.ExprMethodInfo.get_MethodInfo - - - ILLink - IL2075 - member - M:Microsoft.CSharp.RuntimeBinder.Semantics.ExprPropertyInfo.get_PropertyInfo - - - ILLink - IL2075 - member - M:Microsoft.CSharp.RuntimeBinder.SymbolTable.AddPredefinedMethodToSymbolTable(Microsoft.CSharp.RuntimeBinder.Semantics.AggregateSymbol,Microsoft.CSharp.RuntimeBinder.Syntax.Name) - - - ILLink - IL2075 - member - M:Microsoft.CSharp.RuntimeBinder.SymbolTable.AddPredefinedPropertyToSymbolTable(Microsoft.CSharp.RuntimeBinder.Semantics.AggregateSymbol,Microsoft.CSharp.RuntimeBinder.Syntax.Name) - - - ILLink - IL2075 - member - M:Microsoft.CSharp.RuntimeBinder.SymbolTable.BuildDeclarationChain(System.Type) - - - ILLink - IL2080 - member - M:Microsoft.CSharp.RuntimeBinder.SymbolTable.AddNamesInInheritanceHierarchy(System.String,System.Collections.Generic.List{System.Type}) - - - ILLink - IL2026 - member - M:Microsoft.CSharp.RuntimeBinder.ComInterop.CurrencyArgBuilder.Marshal(System.Linq.Expressions.Expression) - - - ILLink - IL2026 - member - M:Microsoft.CSharp.RuntimeBinder.ComInterop.ErrorArgBuilder.Marshal(System.Linq.Expressions.Expression) - - - ILLink - IL2026 - member - M:Microsoft.CSharp.RuntimeBinder.ComInterop.VariantArray.GetStructField(System.Linq.Expressions.ParameterExpression,System.Int32) - - - ILLink - IL2026 - member - M:Microsoft.CSharp.RuntimeBinder.ExpressionTreeCallRewriter.GenerateConvert(Microsoft.CSharp.RuntimeBinder.Semantics.ExprCall) - - - diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Binder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Binder.cs index 0f51633431f562..beec176cb19c60 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Binder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Binder.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Runtime.CompilerServices; @@ -15,6 +16,7 @@ namespace Microsoft.CSharp.RuntimeBinder [EditorBrowsable(EditorBrowsableState.Never)] public static class Binder { + internal const string TrimmerWarning = "Using dynamic types might cause types or members to be removed by trimmer."; ////////////////////////////////////////////////////////////////////// /// @@ -25,6 +27,7 @@ public static class Binder /// The that indicates where this operation is used. /// The sequence of instances for the arguments to this operation. /// Returns a new CSharp binary operation binder. + [RequiresUnreferencedCode(TrimmerWarning)] public static CallSiteBinder BinaryOperation( CSharpBinderFlags flags, ExpressionType operation, @@ -50,8 +53,9 @@ public static CallSiteBinder BinaryOperation( /// /// The flags with which to initialize the binder. /// The type to convert to. - /// The that indicates where this operation is used. + /// The that indicates where this operation is used. /// Returns a new CSharp convert binder. + [RequiresUnreferencedCode(TrimmerWarning)] public static CallSiteBinder Convert( CSharpBinderFlags flags, Type type, @@ -78,6 +82,7 @@ public static CallSiteBinder Convert( /// The that indicates where this operation is used. /// The sequence of instances for the arguments to this operation. /// Returns a new CSharp get index binder. + [RequiresUnreferencedCode(TrimmerWarning)] public static CallSiteBinder GetIndex( CSharpBinderFlags flags, Type? context, @@ -96,6 +101,7 @@ public static CallSiteBinder GetIndex( /// The that indicates where this operation is used. /// The sequence of instances for the arguments to this operation. /// Returns a new CSharp get member binder. + [RequiresUnreferencedCode(TrimmerWarning)] public static CallSiteBinder GetMember( CSharpBinderFlags flags, string name, @@ -112,9 +118,10 @@ public static CallSiteBinder GetMember( /// Initializes a new CSharp invoke binder. /// /// The flags with which to initialize the binder. - /// The that indicates where this operation is used. + /// The that indicates where this operation is used. /// The sequence of instances for the arguments to this operation. /// Returns a new CSharp invoke binder. + [RequiresUnreferencedCode(TrimmerWarning)] public static CallSiteBinder Invoke( CSharpBinderFlags flags, Type? context, @@ -142,6 +149,7 @@ public static CallSiteBinder Invoke( /// The that indicates where this operation is used. /// The sequence of instances for the arguments to this operation. /// Returns a new CSharp invoke member binder. + [RequiresUnreferencedCode(TrimmerWarning)] public static CallSiteBinder InvokeMember( CSharpBinderFlags flags, string name, @@ -179,6 +187,7 @@ public static CallSiteBinder InvokeMember( /// The that indicates where this operation is used. /// The sequence of instances for the arguments to this operation. /// Returns a new CSharp invoke constructor binder. + [RequiresUnreferencedCode(TrimmerWarning)] public static CallSiteBinder InvokeConstructor( CSharpBinderFlags flags, Type? context, @@ -196,6 +205,7 @@ public static CallSiteBinder InvokeConstructor( /// The name of the event to look for. /// The that indicates where this operation is used. /// Returns a new CSharp is event binder. + [RequiresUnreferencedCode(TrimmerWarning)] public static CallSiteBinder IsEvent( CSharpBinderFlags flags, string name, @@ -213,6 +223,7 @@ public static CallSiteBinder IsEvent( /// The that indicates where this operation is used. /// The sequence of instances for the arguments to this operation. /// Returns a new CSharp set index binder. + [RequiresUnreferencedCode(TrimmerWarning)] public static CallSiteBinder SetIndex( CSharpBinderFlags flags, Type? context, @@ -233,6 +244,7 @@ public static CallSiteBinder SetIndex( /// The that indicates where this operation is used. /// The sequence of instances for the arguments to this operation. /// Returns a new CSharp set member binder. + [RequiresUnreferencedCode(TrimmerWarning)] public static CallSiteBinder SetMember( CSharpBinderFlags flags, string name, @@ -254,6 +266,7 @@ public static CallSiteBinder SetMember( /// The that indicates where this operation is used. /// The sequence of instances for the arguments to this operation. /// Returns a new CSharp unary operation binder. + [RequiresUnreferencedCode(TrimmerWarning)] public static CallSiteBinder UnaryOperation( CSharpBinderFlags flags, ExpressionType operation, diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/BinderHelper.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/BinderHelper.cs index 60b1c044f208a2..405c87e1df93ea 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/BinderHelper.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/BinderHelper.cs @@ -11,6 +11,7 @@ using System.Reflection; using System.Numerics.Hashing; using Microsoft.CSharp.RuntimeBinder.Errors; +using System.Diagnostics.CodeAnalysis; namespace Microsoft.CSharp.RuntimeBinder { @@ -19,6 +20,7 @@ internal static class BinderHelper private static MethodInfo s_DoubleIsNaN; private static MethodInfo s_SingleIsNaN; + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal static DynamicMetaObject Bind( ICSharpBinder action, RuntimeBinder binder, diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpBinaryOperationBinder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpBinaryOperationBinder.cs index 91104f1c0240d5..b9387f9d8e6b50 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpBinaryOperationBinder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpBinaryOperationBinder.cs @@ -30,9 +30,11 @@ public string Name public BindingFlag BindingFlags => 0; + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public Expr DispatchPayload(RuntimeBinder runtimeBinder, ArgumentObject[] arguments, LocalVariableSymbol[] locals) => runtimeBinder.BindBinaryOperation(this, arguments, locals); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public void PopulateSymbolTableWithName(Type callingType, ArgumentObject[] arguments) { string name = Operation.GetCLROperatorName(); @@ -67,6 +69,7 @@ public void PopulateSymbolTableWithName(Type callingType, ArgumentObject[] argum /// The flags associated with this binary operation. /// The that indicates where this operation is defined. /// The sequence of instances for the arguments to this operation. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public CSharpBinaryOperationBinder( ExpressionType operation, bool isChecked, @@ -122,6 +125,8 @@ public bool IsEquivalentTo(ICSharpBinder other) /// The right hand side operand of the dynamic binary operation. /// The binding result in case the binding fails, or null. /// The representing the result of the binding. + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This whole class is unsafe. Constructors are marked as such.")] public override DynamicMetaObject FallbackBinaryOperation(DynamicMetaObject target, DynamicMetaObject arg, DynamicMetaObject errorSuggestion) { BinderHelper.ValidateBindArgument(target, nameof(target)); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpConvertBinder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpConvertBinder.cs index f69364fd2602e9..db5e69f3a68a4b 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpConvertBinder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpConvertBinder.cs @@ -29,6 +29,7 @@ public string Name public BindingFlag BindingFlags => 0; + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public Expr DispatchPayload(RuntimeBinder runtimeBinder, ArgumentObject[] arguments, LocalVariableSymbol[] locals) { Debug.Assert(arguments.Length == 1); @@ -37,6 +38,7 @@ public Expr DispatchPayload(RuntimeBinder runtimeBinder, ArgumentObject[] argume : runtimeBinder.BindImplicitConversion(arguments, Type, locals, ConversionKind == CSharpConversionKind.ArrayCreationConversion); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public void PopulateSymbolTableWithName(Type callingType, ArgumentObject[] arguments) { // Conversions don't need to do anything, since they're just conversions! @@ -63,6 +65,7 @@ public void PopulateSymbolTableWithName(Type callingType, ArgumentObject[] argum /// The kind of conversion for this operation. /// True if the operation is defined in a checked context; otherwise, false. /// The that indicates where this operation is defined. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public CSharpConvertBinder( Type type, CSharpConversionKind conversionKind, @@ -113,6 +116,8 @@ public bool IsEquivalentTo(ICSharpBinder other) /// The target of the dynamic convert operation. /// The binding result to use if binding fails, or null. /// The representing the result of the binding. + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This whole class is unsafe. Constructors are marked as such.")] public override DynamicMetaObject FallbackConvert(DynamicMetaObject target, DynamicMetaObject errorSuggestion) { #if ENABLECOMBINDER diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpGetIndexBinder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpGetIndexBinder.cs index b2b793afd79777..e15308e950e1c1 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpGetIndexBinder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpGetIndexBinder.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Dynamic; using Microsoft.CSharp.RuntimeBinder.Errors; using Microsoft.CSharp.RuntimeBinder.Semantics; @@ -19,12 +20,14 @@ internal sealed class CSharpGetIndexBinder : GetIndexBinder, ICSharpBinder public BindingFlag BindingFlags => BindingFlag.BIND_RVALUEREQUIRED; + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public Expr DispatchPayload(RuntimeBinder runtimeBinder, ArgumentObject[] arguments, LocalVariableSymbol[] locals) { Expr indexerArguments = runtimeBinder.CreateArgumentListEXPR(arguments, locals, 1, arguments.Length); return runtimeBinder.BindProperty(this, arguments[0], locals[0], indexerArguments); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public void PopulateSymbolTableWithName(Type callingType, ArgumentObject[] arguments) => SymbolTable.PopulateSymbolTableWithName(SpecialNames.Indexer, null, arguments[0].Type); @@ -43,6 +46,7 @@ public void PopulateSymbolTableWithName(Type callingType, ArgumentObject[] argum /// /// The that indicates where this operation is defined. /// The sequence of instances for the arguments to this operation. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public CSharpGetIndexBinder( Type callingContext, IEnumerable argumentInfo) : @@ -85,6 +89,8 @@ public bool IsEquivalentTo(ICSharpBinder other) /// The arguments of the dynamic get index operation. /// The binding result to use if binding fails, or null. /// The representing the result of the binding. + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This whole class is unsafe. Constructors are marked as such.")] public override DynamicMetaObject FallbackGetIndex(DynamicMetaObject target, DynamicMetaObject[] indexes, DynamicMetaObject errorSuggestion) { #if ENABLECOMBINDER diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpGetMemberBinder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpGetMemberBinder.cs index a0a59d1046bd65..cdfe66e1c553ad 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpGetMemberBinder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpGetMemberBinder.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Dynamic; using System.Numerics.Hashing; using Microsoft.CSharp.RuntimeBinder.Errors; @@ -19,12 +20,14 @@ internal sealed class CSharpGetMemberBinder : GetMemberBinder, IInvokeOnGetBinde { public BindingFlag BindingFlags => BindingFlag.BIND_RVALUEREQUIRED; + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public Expr DispatchPayload(RuntimeBinder runtimeBinder, ArgumentObject[] arguments, LocalVariableSymbol[] locals) { Debug.Assert(arguments.Length == 1); return runtimeBinder.BindProperty(this, arguments[0], locals[0], null); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public void PopulateSymbolTableWithName(Type callingType, ArgumentObject[] arguments) => SymbolTable.PopulateSymbolTableWithName(Name, null, arguments[0].Type); @@ -49,6 +52,7 @@ public void PopulateSymbolTableWithName(Type callingType, ArgumentObject[] argum /// Determines if COM binder should return a callable object. /// The that indicates where this operation is defined. /// The sequence of instances for the arguments to this operation. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public CSharpGetMemberBinder( string name, bool resultIndexed, @@ -100,6 +104,8 @@ public bool IsEquivalentTo(ICSharpBinder other) /// The target of the dynamic get member operation. /// The binding result to use if binding fails, or null. /// The representing the result of the binding. + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This whole class is unsafe. Constructors are marked as such.")] public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion) { #if ENABLECOMBINDER diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpInvokeBinder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpInvokeBinder.cs index dd25a30c4dbcd4..db5ed08951c367 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpInvokeBinder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpInvokeBinder.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Dynamic; using System.Numerics.Hashing; using Microsoft.CSharp.RuntimeBinder.Errors; @@ -18,9 +19,11 @@ internal sealed class CSharpInvokeBinder : InvokeBinder, ICSharpInvokeOrInvokeMe { public BindingFlag BindingFlags => 0; + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public Expr DispatchPayload(RuntimeBinder runtimeBinder, ArgumentObject[] arguments, LocalVariableSymbol[] locals) => runtimeBinder.DispatchPayload(this, arguments, locals); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public void PopulateSymbolTableWithName(Type callingType, ArgumentObject[] arguments) => RuntimeBinder.PopulateSymbolTableWithPayloadInformation(this, callingType, arguments); @@ -52,6 +55,7 @@ public void PopulateSymbolTableWithName(Type callingType, ArgumentObject[] argum /// Extra information about this operation that is not specific to any particular argument. /// The that indicates where this operation is defined. /// The sequence of instances for the arguments to this operation. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public CSharpInvokeBinder( CSharpCallFlags flags, Type callingContext, @@ -98,6 +102,8 @@ public bool IsEquivalentTo(ICSharpBinder other) /// The arguments of the dynamic invoke operation. /// The binding result to use if binding fails, or null. /// The representing the result of the binding. + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This whole class is unsafe. Constructors are marked as such.")] public override DynamicMetaObject FallbackInvoke(DynamicMetaObject target, DynamicMetaObject[] args, DynamicMetaObject errorSuggestion) { #if ENABLECOMBINDER diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpInvokeConstructorBinder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpInvokeConstructorBinder.cs index f4c1d1cacc918b..1b827363eb4f43 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpInvokeConstructorBinder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpInvokeConstructorBinder.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Dynamic; using System.Numerics.Hashing; using Microsoft.CSharp.RuntimeBinder.Semantics; @@ -13,9 +14,11 @@ internal sealed class CSharpInvokeConstructorBinder : DynamicMetaObjectBinder, I { public BindingFlag BindingFlags => 0; + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public Expr DispatchPayload(RuntimeBinder runtimeBinder, ArgumentObject[] arguments, LocalVariableSymbol[] locals) => runtimeBinder.DispatchPayload(this, arguments, locals); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public void PopulateSymbolTableWithName(Type callingType, ArgumentObject[] arguments) => RuntimeBinder.PopulateSymbolTableWithPayloadInformation(this, callingType, arguments); @@ -39,6 +42,7 @@ public void PopulateSymbolTableWithName(Type callingType, ArgumentObject[] argum private readonly Type _callingContext; + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public CSharpInvokeConstructorBinder( CSharpCallFlags flags, Type callingContext, @@ -81,6 +85,8 @@ public bool IsEquivalentTo(ICSharpBinder other) return BinderHelper.CompareArgInfos(TypeArguments, otherBinder.TypeArguments, _argumentInfo, otherBinder._argumentInfo); } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This whole class is unsafe. Constructors are marked as such.")] public override DynamicMetaObject Bind(DynamicMetaObject target, DynamicMetaObject[] args) { BinderHelper.ValidateBindArgument(target, nameof(target)); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpInvokeMemberBinder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpInvokeMemberBinder.cs index 839be81cd4500a..b48409abc186c9 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpInvokeMemberBinder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpInvokeMemberBinder.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; using System.Dynamic; using System.Numerics.Hashing; using Microsoft.CSharp.RuntimeBinder.Errors; @@ -19,9 +20,11 @@ internal sealed class CSharpInvokeMemberBinder : InvokeMemberBinder, ICSharpInvo { public BindingFlag BindingFlags => 0; + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public Expr DispatchPayload(RuntimeBinder runtimeBinder, ArgumentObject[] arguments, LocalVariableSymbol[] locals) => runtimeBinder.DispatchPayload(this, arguments, locals); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public void PopulateSymbolTableWithName(Type callingType, ArgumentObject[] arguments) => RuntimeBinder.PopulateSymbolTableWithPayloadInformation(this, callingType, arguments); @@ -58,6 +61,7 @@ public CSharpArgumentInfo[] ArgumentInfoArray() /// The that indicates where this operation is defined. /// The list of user-specified type arguments to this call. /// The sequence of instances for the arguments to this operation. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public CSharpInvokeMemberBinder( CSharpCallFlags flags, string name, @@ -111,6 +115,8 @@ public bool IsEquivalentTo(ICSharpBinder other) /// The arguments of the dynamic invoke member operation. /// The binding result to use if binding fails, or null. /// The representing the result of the binding. + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This whole class is unsafe. Constructors are marked as such.")] public override DynamicMetaObject FallbackInvokeMember(DynamicMetaObject target, DynamicMetaObject[] args, DynamicMetaObject errorSuggestion) { #if ENABLECOMBINDER @@ -135,6 +141,8 @@ public override DynamicMetaObject FallbackInvokeMember(DynamicMetaObject target, /// The arguments of the dynamic invoke operation. /// The binding result to use if binding fails, or null. /// The representing the result of the binding. + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This whole class is unsafe. Constructors are marked as such.")] public override DynamicMetaObject FallbackInvoke(DynamicMetaObject target, DynamicMetaObject[] args, DynamicMetaObject errorSuggestion) { CSharpInvokeBinder c = new CSharpInvokeBinder(Flags, CallingContext, _argumentInfo).TryGetExisting(); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpIsEventBinder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpIsEventBinder.cs index 3faaa5323667db..83c8ad8703067a 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpIsEventBinder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpIsEventBinder.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics.CodeAnalysis; using System.Dynamic; using System.Numerics.Hashing; using Microsoft.CSharp.RuntimeBinder.Semantics; @@ -15,9 +16,11 @@ internal sealed class CSharpIsEventBinder : DynamicMetaObjectBinder, ICSharpBind { public BindingFlag BindingFlags => 0; + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public Expr DispatchPayload(RuntimeBinder runtimeBinder, ArgumentObject[] arguments, LocalVariableSymbol[] locals) => runtimeBinder.BindIsEvent(this, arguments, locals); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public void PopulateSymbolTableWithName(Type callingType, ArgumentObject[] arguments) => SymbolTable.PopulateSymbolTableWithName(Name, null, arguments[0].Info.IsStaticType ? arguments[0].Value as Type : arguments[0].Type); @@ -36,6 +39,7 @@ public void PopulateSymbolTableWithName(Type callingType, ArgumentObject[] argum /// /// The name of the member to test. /// The that indicates where this operation is defined. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public CSharpIsEventBinder( string name, Type callingContext) @@ -80,6 +84,8 @@ public bool IsEquivalentTo(ICSharpBinder other) /// The target of the dynamic binary operation. /// The arguments to the dynamic event test. /// The representing the result of the binding. + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This whole class is unsafe. Constructors are marked as such.")] public override DynamicMetaObject Bind(DynamicMetaObject target, DynamicMetaObject[] args) { BinderHelper.ValidateBindArgument(target, nameof(target)); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpSetIndexBinder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpSetIndexBinder.cs index 5c6d3dc98b72af..ef53ab6f4ccf09 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpSetIndexBinder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpSetIndexBinder.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Dynamic; using System.Numerics.Hashing; using Microsoft.CSharp.RuntimeBinder.Errors; @@ -20,9 +21,11 @@ internal sealed class CSharpSetIndexBinder : SetIndexBinder, ICSharpBinder public BindingFlag BindingFlags => 0; + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public Expr DispatchPayload(RuntimeBinder runtimeBinder, ArgumentObject[] arguments, LocalVariableSymbol[] locals) => runtimeBinder.BindAssignment(this, arguments, locals); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public void PopulateSymbolTableWithName(Type callingType, ArgumentObject[] arguments) => SymbolTable.PopulateSymbolTableWithName(SpecialNames.Indexer, null, arguments[0].Type); @@ -49,6 +52,7 @@ public void PopulateSymbolTableWithName(Type callingType, ArgumentObject[] argum /// True if the operation is defined in a checked context; otherwise, false. /// The that indicates where this operation is defined. /// The sequence of instances for the arguments to this operation. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public CSharpSetIndexBinder( bool isCompoundAssignment, bool isChecked, @@ -105,6 +109,8 @@ public bool IsEquivalentTo(ICSharpBinder other) /// The value to set to the collection. /// The binding result to use if binding fails, or null. /// The representing the result of the binding. + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This whole class is unsafe. Constructors are marked as such.")] public override DynamicMetaObject FallbackSetIndex(DynamicMetaObject target, DynamicMetaObject[] indexes, DynamicMetaObject value, DynamicMetaObject errorSuggestion) { #if ENABLECOMBINDER diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpSetMemberBinder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpSetMemberBinder.cs index 3ae5888c9e0ca2..4dff5e5ba1ed86 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpSetMemberBinder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpSetMemberBinder.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Dynamic; using System.Numerics.Hashing; using Microsoft.CSharp.RuntimeBinder.Errors; @@ -18,9 +19,11 @@ internal sealed class CSharpSetMemberBinder : SetMemberBinder, ICSharpBinder { public BindingFlag BindingFlags => 0; + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public Expr DispatchPayload(RuntimeBinder runtimeBinder, ArgumentObject[] arguments, LocalVariableSymbol[] locals) => runtimeBinder.BindAssignment(this, arguments, locals); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public void PopulateSymbolTableWithName(Type callingType, ArgumentObject[] arguments) => SymbolTable.PopulateSymbolTableWithName(Name, null, arguments[0].Type); @@ -47,6 +50,7 @@ public void PopulateSymbolTableWithName(Type callingType, ArgumentObject[] argum /// True if the operation is defined in a checked context; otherwise, false. /// The that indicates where this operation is defined. /// The sequence of instances for the arguments to this operation. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public CSharpSetMemberBinder( string name, bool isCompoundAssignment, @@ -105,6 +109,8 @@ public bool IsEquivalentTo(ICSharpBinder other) /// The value to set to the member. /// The binding result to use if binding fails, or null. /// The representing the result of the binding. + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This whole class is unsafe. Constructors are marked as such.")] public override DynamicMetaObject FallbackSetMember(DynamicMetaObject target, DynamicMetaObject value, DynamicMetaObject errorSuggestion) { #if ENABLECOMBINDER diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpUnaryOperationBinder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpUnaryOperationBinder.cs index 04c6918d221147..b86a6994e51667 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpUnaryOperationBinder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpUnaryOperationBinder.cs @@ -31,9 +31,11 @@ public string Name public BindingFlag BindingFlags => 0; + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public Expr DispatchPayload(RuntimeBinder runtimeBinder, ArgumentObject[] arguments, LocalVariableSymbol[] locals) => runtimeBinder.BindUnaryOperation(this, arguments, locals); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public void PopulateSymbolTableWithName(Type callingType, ArgumentObject[] arguments) => SymbolTable.PopulateSymbolTableWithName(Operation.GetCLROperatorName(), null, arguments[0].Type); @@ -56,6 +58,7 @@ public void PopulateSymbolTableWithName(Type callingType, ArgumentObject[] argum /// True if the operation is defined in a checked context; otherwise, false. /// The that indicates where this operation is defined. /// The sequence of instances for the arguments to this operation. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public CSharpUnaryOperationBinder( ExpressionType operation, bool isChecked, @@ -106,6 +109,8 @@ public bool IsEquivalentTo(ICSharpBinder other) /// The target of the dynamic unary operation. /// The binding result in case the binding fails, or null. /// The representing the result of the binding. + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This whole class is unsafe. Constructors are marked as such.")] public override DynamicMetaObject FallbackUnaryOperation(DynamicMetaObject target, DynamicMetaObject errorSuggestion) { BinderHelper.ValidateBindArgument(target, nameof(target)); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ArgBuilder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ArgBuilder.cs index cffbc296e5242c..33cb94053f1074 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ArgBuilder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ArgBuilder.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; namespace Microsoft.CSharp.RuntimeBinder.ComInterop @@ -16,12 +17,14 @@ internal abstract class ArgBuilder /// /// Provides the Expression which provides the value to be passed to the argument. /// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal abstract Expression Marshal(Expression parameter); /// /// Provides the Expression which provides the value to be passed to the argument. /// This method is called when result is intended to be used ByRef. /// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal virtual Expression MarshalToRef(Expression parameter) { return Marshal(parameter); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/BoolArgBuilder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/BoolArgBuilder.cs index f217f47e4f6720..ab7564c02cac5e 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/BoolArgBuilder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/BoolArgBuilder.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; namespace Microsoft.CSharp.RuntimeBinder.ComInterop @@ -15,6 +16,7 @@ internal BoolArgBuilder(Type parameterType) Debug.Assert(parameterType == typeof(bool)); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal override Expression MarshalToRef(Expression parameter) { // parameter ? -1 : 0 diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/BoundDispEvent.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/BoundDispEvent.cs index 8333155e80000d..354dcc7f9aaba0 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/BoundDispEvent.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/BoundDispEvent.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics.CodeAnalysis; using System.Dynamic; using System.Linq.Expressions; using System.Runtime.InteropServices; @@ -14,6 +15,7 @@ internal sealed class BoundDispEvent : DynamicObject private readonly Guid _sourceIid; private readonly int _dispid; + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal BoundDispEvent(object rcw, Guid sourceIid, int dispid) { _rcw = rcw; @@ -28,6 +30,8 @@ internal BoundDispEvent(object rcw, Guid sourceIid, int dispid) /// The handler for the operation. /// The result of the operation. /// true if the operation is complete, false if the call site should determine behavior. + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This whole class is unsafe. Constructors are marked as such.")] public override bool TryBinaryOperation(BinaryOperationBinder binder, object handler, out object result) { if (binder.Operation == ExpressionType.AddAssign) @@ -71,6 +75,7 @@ private static void VerifyHandler(object handler) /// /// The handler to be added. /// The original event with handler added. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private object InPlaceAdd(object handler) { Requires.NotNull(handler, nameof(handler)); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComBinder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComBinder.cs index e8d9ec19aedce3..26588e6a4093ae 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComBinder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComBinder.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Dynamic; using System.Linq.Expressions; using System.Runtime.InteropServices; @@ -31,6 +32,7 @@ public static bool IsComObject(object value) /// The new representing the result of the binding. /// true if member evaluation may be delayed. /// true if operation was bound successfully; otherwise, false. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static bool TryBindGetMember(GetMemberBinder binder, DynamicMetaObject instance, out DynamicMetaObject result, bool delayInvocation) { Requires.NotNull(binder, nameof(binder)); @@ -62,6 +64,7 @@ public static bool TryBindGetMember(GetMemberBinder binder, DynamicMetaObject in /// The representing the value for the set member operation. /// The new representing the result of the binding. /// true if operation was bound successfully; otherwise, false. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static bool TryBindSetMember(SetMemberBinder binder, DynamicMetaObject instance, DynamicMetaObject value, out DynamicMetaObject result) { Requires.NotNull(binder, nameof(binder)); @@ -86,6 +89,7 @@ public static bool TryBindSetMember(SetMemberBinder binder, DynamicMetaObject in /// An array of instances - arguments to the invoke member operation. /// The new representing the result of the binding. /// true if operation was bound successfully; otherwise, false. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static bool TryBindInvoke(InvokeBinder binder, DynamicMetaObject instance, DynamicMetaObject[] args, out DynamicMetaObject result) { Requires.NotNull(binder, nameof(binder)); @@ -110,6 +114,7 @@ public static bool TryBindInvoke(InvokeBinder binder, DynamicMetaObject instance /// An array of instances - arguments to the invoke member operation. /// The new representing the result of the binding. /// true if operation was bound successfully; otherwise, false. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static bool TryBindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject instance, DynamicMetaObject[] args, out DynamicMetaObject result) { Requires.NotNull(binder, nameof(binder)); @@ -134,6 +139,7 @@ public static bool TryBindInvokeMember(InvokeMemberBinder binder, DynamicMetaObj /// An array of instances - arguments to the invoke member operation. /// The new representing the result of the binding. /// true if operation was bound successfully; otherwise, false. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static bool TryBindGetIndex(GetIndexBinder binder, DynamicMetaObject instance, DynamicMetaObject[] args, out DynamicMetaObject result) { Requires.NotNull(binder, nameof(binder)); @@ -159,6 +165,7 @@ public static bool TryBindGetIndex(GetIndexBinder binder, DynamicMetaObject inst /// The representing the value for the set index operation. /// The new representing the result of the binding. /// true if operation was bound successfully; otherwise, false. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static bool TryBindSetIndex(SetIndexBinder binder, DynamicMetaObject instance, DynamicMetaObject[] args, DynamicMetaObject value, out DynamicMetaObject result) { Requires.NotNull(binder, nameof(binder)); @@ -220,6 +227,7 @@ public static bool TryConvert(ConvertBinder binder, DynamicMetaObject instance, /// /// The object for which member names are requested. /// The collection of member names. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal static IList GetDynamicDataMemberNames(object value) { Requires.NotNull(value, nameof(value)); @@ -235,6 +243,7 @@ internal static IList GetDynamicDataMemberNames(object value) /// The object for which data members are requested. /// The enumeration of names of data members for which to retrieve values. /// The collection of pairs that represent data member's names and their data. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal static IList> GetDynamicDataMembers(object value, IEnumerable names) { Requires.NotNull(value, nameof(value)); @@ -243,6 +252,7 @@ internal static IList> GetDynamicDataMembers(object return ComObject.ObjectToComObject(value).GetMembers(names); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static bool TryGetMetaObject(ref DynamicMetaObject instance) { // If we're already a COM MO don't make a new one @@ -261,6 +271,7 @@ private static bool TryGetMetaObject(ref DynamicMetaObject instance) return false; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static bool TryGetMetaObjectInvoke(ref DynamicMetaObject instance) { // If we're already a COM MO don't make a new one diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComBinderHelpers.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComBinderHelpers.cs index 1ce21e788fb0a5..f1b73bd86e970c 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComBinderHelpers.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComBinderHelpers.cs @@ -5,6 +5,7 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Dynamic; using System.Linq.Expressions; using System.Runtime.CompilerServices; @@ -37,6 +38,7 @@ internal static bool IsByRef(DynamicMetaObject mo) return mo.Expression is ParameterExpression pe && pe.IsByRef; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal static bool IsStrongBoxArg(DynamicMetaObject o) { Type t = o.LimitType; @@ -45,6 +47,7 @@ internal static bool IsStrongBoxArg(DynamicMetaObject o) // This helper prepares arguments for COM binding by transforming ByVal StrongBox arguments // into ByRef expressions that represent the argument's Value fields. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal static bool[] ProcessArgumentsForCom(ref DynamicMetaObject[] args) { Debug.Assert(args != null); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComClassMetaObject.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComClassMetaObject.cs index c61fc467d991ab..ee0e2f5b75b312 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComClassMetaObject.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComClassMetaObject.cs @@ -2,17 +2,21 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Dynamic; +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; namespace Microsoft.CSharp.RuntimeBinder.ComInterop { internal sealed class ComClassMetaObject : DynamicMetaObject { + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal ComClassMetaObject(Expression expression, ComTypeClassDesc cls) : base(expression, BindingRestrictions.Empty, cls) { } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This whole class is unsafe. Constructors are marked as such.")] public override DynamicMetaObject BindCreateInstance(CreateInstanceBinder binder, DynamicMetaObject[] args) { return new DynamicMetaObject( diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComEventsSink.Extended.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComEventsSink.Extended.cs index c6bd8a9c459c39..b68929a5fa2161 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComEventsSink.Extended.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComEventsSink.Extended.cs @@ -2,7 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Microsoft.CSharp.RuntimeBinder; using Microsoft.CSharp.RuntimeBinder.ComInterop; namespace System.Runtime.InteropServices @@ -15,6 +17,7 @@ private void Initialize(object rcw, Guid iid) Advise(rcw); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public void AddHandler(int dispid, object func) { ComEventsMethod method = FindMethod(dispid); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComInvokeAction.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComInvokeAction.cs index 62f2685e5a2d1b..e804bbaabe8d50 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComInvokeAction.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComInvokeAction.cs @@ -4,6 +4,7 @@ using System; using System.Collections.ObjectModel; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Dynamic; using System.Linq.Expressions; using System.Runtime.CompilerServices; @@ -15,11 +16,14 @@ namespace Microsoft.CSharp.RuntimeBinder.ComInterop /// internal sealed class ComInvokeAction : InvokeBinder { + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal ComInvokeAction(CallInfo callInfo) : base(callInfo) { } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This whole class is unsafe. Constructors are marked as such.")] public override DynamicMetaObject FallbackInvoke(DynamicMetaObject target, DynamicMetaObject[] args, DynamicMetaObject errorSuggestion) { if (ComBinder.TryBindInvoke(this, target, args, out DynamicMetaObject res)) @@ -46,9 +50,19 @@ public override DynamicMetaObject FallbackInvoke(DynamicMetaObject target, Dynam /// internal sealed class SplatInvokeBinder : CallSiteBinder { - internal static readonly SplatInvokeBinder s_instance = new SplatInvokeBinder(); + private static readonly SplatInvokeBinder s_instance = new SplatInvokeBinder(); + + internal static SplatInvokeBinder Instance + { + [RequiresUnreferencedCode(Binder.TrimmerWarning)] + get => s_instance; + } + + private SplatInvokeBinder() { } // Just splat the args and dispatch through a nested site + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This whole class is unsafe. The only entry-point to this class is through Instance property which is marked as unsafe.")] public override Expression Bind(object[] args, ReadOnlyCollection parameters, LabelTarget returnLabel) { Debug.Assert(args.Length == 2); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComInvokeBinder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComInvokeBinder.cs index 1c7737801ecbb8..343684efe133b2 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComInvokeBinder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComInvokeBinder.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Dynamic; using System.Linq.Expressions; using System.Runtime.InteropServices; @@ -148,6 +149,7 @@ private static Type MarshalType(DynamicMetaObject mo, bool isByRef) return marshalType; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal DynamicMetaObject Invoke() { _keywordArgNames = _callInfo.ArgumentNames.ToArray(); @@ -191,6 +193,7 @@ private Expression CreateScope(Expression expression) return vars.Count > 0 ? Expression.Block(vars, expression) : expression; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expression GenerateTryBlock() { // @@ -395,6 +398,7 @@ private Expression GenerateFinallyBlock() /// Create a stub for the target of the optimized lopop. /// /// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expression MakeIDispatchInvokeTarget() { Debug.Assert(_varEnumSelector.VariantBuilders.Length == _totalExplicitArgs); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComMetaObject.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComMetaObject.cs index e27839d60935b5..7ae60b32a645e6 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComMetaObject.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComMetaObject.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; using System.Dynamic; using System.Linq.Expressions; @@ -9,6 +10,7 @@ namespace Microsoft.CSharp.RuntimeBinder.ComInterop // Note: we only need to support the operations used by ComBinder internal sealed class ComMetaObject : DynamicMetaObject { + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal ComMetaObject(Expression expression, BindingRestrictions restrictions, object arg) : base(expression, restrictions, arg) { @@ -50,6 +52,8 @@ public override DynamicMetaObject BindSetIndex(SetIndexBinder binder, DynamicMet return binder.Defer(WrapSelf(), indexes.AddLast(value)); } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This whole class is unsafe. Constructors are marked as such.")] private DynamicMetaObject WrapSelf() { return new DynamicMetaObject( diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComObject.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComObject.cs index b4724ca2fccdce..fd4aa80ca23a7f 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComObject.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComObject.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Dynamic; using System.Linq.Expressions; using System.Reflection; @@ -30,6 +31,7 @@ internal ComObject(object rcw) /// Gets a that wraps the runtime-callable-wrapper, or creates one if none currently exists. /// /// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static ComObject ObjectToComObject(object rcw) { Debug.Assert(ComBinder.IsComObject(rcw)); @@ -70,6 +72,7 @@ internal static MemberExpression RcwFromComObject(Expression comObject) } // Expression that finds or creates a ComObject that corresponds to given Rcw + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal static MethodCallExpression RcwToComObject(Expression rcw) { return Expression.Call( @@ -78,6 +81,7 @@ internal static MethodCallExpression RcwToComObject(Expression rcw) ); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static ComObject CreateComObject(object rcw) { if (rcw is IDispatch dispatchObject) @@ -90,11 +94,13 @@ private static ComObject CreateComObject(object rcw) return new ComObject(rcw); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal virtual IList GetMemberNames(bool dataOnly) { return Array.Empty(); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal virtual IList> GetMembers(IEnumerable names) { return Array.Empty>(); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComRuntimeHelpers.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComRuntimeHelpers.cs index 77363c66ba54d5..62ff9e0023fff9 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComRuntimeHelpers.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComRuntimeHelpers.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Reflection.Emit; @@ -14,6 +15,7 @@ namespace Microsoft.CSharp.RuntimeBinder.ComInterop { internal static class ComRuntimeHelpers { + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static void CheckThrowException(int hresult, ref ExcepInfo excepInfo, uint argErr, string message) { if (ComHresults.IsSuccess(hresult)) @@ -205,6 +207,7 @@ internal static ComTypes.TYPELIBATTR GetTypeAttrForTypeLib(ComTypes.ITypeLib typ } } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static BoundDispEvent CreateComEvent(object rcw, Guid sourceIid, int dispid) { return new BoundDispEvent(rcw, sourceIid, dispid); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComTypeClassDesc.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComTypeClassDesc.cs index 3fa5ca8c7c754b..6b71421d75f35e 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComTypeClassDesc.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComTypeClassDesc.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Dynamic; using System.Linq.Expressions; using ComTypes = System.Runtime.InteropServices.ComTypes; @@ -15,6 +16,7 @@ internal sealed class ComTypeClassDesc : ComTypeDesc, IDynamicMetaObjectProvider private LinkedList _sourceItfs; // source interfaces supported by this coclass private Type _typeObj; + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public object CreateInstance() { if (_typeObj == null) @@ -24,6 +26,7 @@ public object CreateInstance() return Activator.CreateInstance(Type.GetTypeFromCLSID(Guid)); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal ComTypeClassDesc(ComTypes.ITypeInfo typeInfo, ComTypeLibDesc typeLibDesc) : base(typeInfo, typeLibDesc) { @@ -74,6 +77,8 @@ internal bool Implements(string itfName, bool isSourceItf) #region IDynamicMetaObjectProvider Members + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This whole class is unsafe. Constructors are marked as such.")] public DynamicMetaObject GetMetaObject(Expression parameter) { return new ComClassMetaObject(parameter, this); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComTypeDesc.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComTypeDesc.cs index ac92ab9104f542..e426dfc344b7ac 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComTypeDesc.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComTypeDesc.cs @@ -4,6 +4,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices.ComTypes; using System.Threading; @@ -25,6 +26,7 @@ internal ComTypeDesc(ITypeInfo typeInfo, ComTypeLibDesc typeLibDesc) TypeLib = typeLibDesc; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal static ComTypeDesc FromITypeInfo(ITypeInfo typeInfo, TYPEATTR typeAttr) { switch (typeAttr.typekind) diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComTypeLibDesc.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComTypeLibDesc.cs index 522ce0135eafcd..591d8c59309d6e 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComTypeLibDesc.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComTypeLibDesc.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Dynamic; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq.Expressions; using System.Runtime.InteropServices; @@ -48,6 +49,7 @@ DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression parameter) #endregion + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal static ComTypeLibDesc GetFromTypeLib(ComTypes.ITypeLib typeLib) { // check whether we have already loaded this type library diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ConversionArgBuilder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ConversionArgBuilder.cs index 6958c6a0223c8e..2c7f85db3b9e95 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ConversionArgBuilder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ConversionArgBuilder.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; namespace Microsoft.CSharp.RuntimeBinder.ComInterop @@ -17,11 +18,13 @@ internal ConversionArgBuilder(Type parameterType, SimpleArgBuilder innerBuilder) _innerBuilder = innerBuilder; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal override Expression Marshal(Expression parameter) { return _innerBuilder.Marshal(Helpers.Convert(parameter, _parameterType)); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal override Expression MarshalToRef(Expression parameter) { //we are not supporting conversion InOut diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ConvertArgBuilder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ConvertArgBuilder.cs index 668d269ba2ed41..15357e4f4fe0ec 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ConvertArgBuilder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ConvertArgBuilder.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; namespace Microsoft.CSharp.RuntimeBinder.ComInterop @@ -16,6 +17,7 @@ internal ConvertArgBuilder(Type parameterType, Type marshalType) _marshalType = marshalType; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal override Expression Marshal(Expression parameter) { parameter = base.Marshal(parameter); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ConvertibleArgBuilder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ConvertibleArgBuilder.cs index 7e18343a4eaf48..69c95e0d2799c7 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ConvertibleArgBuilder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ConvertibleArgBuilder.cs @@ -2,17 +2,20 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; namespace Microsoft.CSharp.RuntimeBinder.ComInterop { internal sealed class ConvertibleArgBuilder : ArgBuilder { + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal override Expression Marshal(Expression parameter) { return Helpers.Convert(parameter, typeof(IConvertible)); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal override Expression MarshalToRef(Expression parameter) { //we are not supporting convertible InOut diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/CurrencyArgBuilder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/CurrencyArgBuilder.cs index 9422fee88eecc8..dd864e1ac5d88b 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/CurrencyArgBuilder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/CurrencyArgBuilder.cs @@ -5,6 +5,7 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Runtime.InteropServices; @@ -18,6 +19,7 @@ internal CurrencyArgBuilder(Type parameterType) Debug.Assert(parameterType == typeof(CurrencyWrapper)); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal override Expression Marshal(Expression parameter) { // parameter.WrappedObject @@ -27,6 +29,7 @@ internal override Expression Marshal(Expression parameter) ); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal override Expression MarshalToRef(Expression parameter) { // Decimal.ToOACurrency(parameter.WrappedObject) diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/DateTimeArgBuilder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/DateTimeArgBuilder.cs index 124ba097e83acd..f130b237bf0efe 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/DateTimeArgBuilder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/DateTimeArgBuilder.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; namespace Microsoft.CSharp.RuntimeBinder.ComInterop @@ -15,6 +16,7 @@ internal DateTimeArgBuilder(Type parameterType) Debug.Assert(parameterType == typeof(DateTime)); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal override Expression MarshalToRef(Expression parameter) { // parameter.ToOADate() diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/DispCallable.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/DispCallable.cs index d4ec6cb6b168e8..3a0b4678f4ebab 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/DispCallable.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/DispCallable.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; using System.Dynamic; using System.Globalization; using System.Linq.Expressions; @@ -29,6 +30,7 @@ internal DispCallable(IDispatchComObject dispatch, string memberName, int dispId public int DispId { get; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public DynamicMetaObject GetMetaObject(Expression parameter) { return new DispCallableMetaObject(parameter, this); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/DispCallableMetaObject.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/DispCallableMetaObject.cs index 57ad83ac75f92b..6d8f38598a0eb3 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/DispCallableMetaObject.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/DispCallableMetaObject.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; using System.Dynamic; using System.Linq.Expressions; @@ -10,6 +11,7 @@ internal sealed class DispCallableMetaObject : DynamicMetaObject { private readonly DispCallable _callable; + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal DispCallableMetaObject(Expression expression, DispCallable callable) : base(expression, BindingRestrictions.Empty, callable) { @@ -28,6 +30,8 @@ public override DynamicMetaObject BindInvoke(InvokeBinder binder, DynamicMetaObj base.BindInvoke(binder, args); } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This whole class is unsafe. Constructors are marked as such.")] private DynamicMetaObject BindGetOrInvoke(DynamicMetaObject[] args, CallInfo callInfo) { IDispatchComObject target = _callable.DispatchComObject; @@ -43,6 +47,8 @@ private DynamicMetaObject BindGetOrInvoke(DynamicMetaObject[] args, CallInfo cal return null; } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This whole class is unsafe. Constructors are marked as such.")] public override DynamicMetaObject BindSetIndex(SetIndexBinder binder, DynamicMetaObject[] indexes, DynamicMetaObject value) { IDispatchComObject target = _callable.DispatchComObject; @@ -67,6 +73,7 @@ public override DynamicMetaObject BindSetIndex(SetIndexBinder binder, DynamicMet return base.BindSetIndex(binder, indexes, value); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private DynamicMetaObject BindComInvoke(ComMethodDesc method, DynamicMetaObject[] indexes, CallInfo callInfo, bool[] isByRef) { Expression callable = Expression; @@ -86,6 +93,7 @@ private DynamicMetaObject BindComInvoke(ComMethodDesc method, DynamicMetaObject[ ).Invoke(); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private BindingRestrictions DispCallableRestrictions() { Expression callable = Expression; diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/DispatchArgBuilder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/DispatchArgBuilder.cs index a44fe5799a8788..6d2bcdf4475453 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/DispatchArgBuilder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/DispatchArgBuilder.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Runtime.InteropServices; @@ -17,6 +18,7 @@ internal DispatchArgBuilder(Type parameterType) _isWrapper = parameterType == typeof(DispatchWrapper); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal override Expression Marshal(Expression parameter) { parameter = base.Marshal(parameter); @@ -33,6 +35,7 @@ internal override Expression Marshal(Expression parameter) return Helpers.Convert(parameter, typeof(object)); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal override Expression MarshalToRef(Expression parameter) { parameter = Marshal(parameter); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ErrorArgBuilder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ErrorArgBuilder.cs index a82f7099439571..5a5cdbd7540c52 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ErrorArgBuilder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ErrorArgBuilder.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Runtime.InteropServices; @@ -16,6 +17,7 @@ internal ErrorArgBuilder(Type parameterType) Debug.Assert(parameterType == typeof(ErrorWrapper)); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal override Expression Marshal(Expression parameter) { // parameter.ErrorCode diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ExcepInfo.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ExcepInfo.cs index 9f9276291ad482..0426df6712f4d1 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ExcepInfo.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ExcepInfo.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics.CodeAnalysis; using System.Diagnostics; using System.Reflection; using System.Runtime.InteropServices; @@ -45,6 +46,7 @@ private static string ConvertAndFreeBstr(ref IntPtr bstr) return result; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal Exception GetException() { Debug.Assert(pfnDeferredFillIn == IntPtr.Zero); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/IDispatchComObject.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/IDispatchComObject.cs index 3d8371d0d5188d..cc7f6cece9ddcc 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/IDispatchComObject.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/IDispatchComObject.cs @@ -5,6 +5,7 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Dynamic; using System.Linq.Expressions; using System.Globalization; @@ -78,6 +79,7 @@ internal sealed class IDispatchComObject : ComObject, IDynamicMetaObjectProvider private ComTypeDesc _comTypeDesc; private static readonly Dictionary s_cacheComTypeDesc = new Dictionary(); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal IDispatchComObject(IDispatch rcw) : base(rcw) { @@ -128,6 +130,7 @@ private static int GetIDsOfNames(IDispatch dispatch, string name, out int dispId return hresult; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal bool TryGetGetItem(out ComMethodDesc value) { ComMethodDesc methodDesc = _comTypeDesc.GetItem; @@ -140,6 +143,7 @@ internal bool TryGetGetItem(out ComMethodDesc value) return SlowTryGetGetItem(out value); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool SlowTryGetGetItem(out ComMethodDesc value) { EnsureScanDefinedMethods(); @@ -159,6 +163,7 @@ private bool SlowTryGetGetItem(out ComMethodDesc value) return true; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal bool TryGetSetItem(out ComMethodDesc value) { ComMethodDesc methodDesc = _comTypeDesc.SetItem; @@ -171,6 +176,7 @@ internal bool TryGetSetItem(out ComMethodDesc value) return SlowTryGetSetItem(out value); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool SlowTryGetSetItem(out ComMethodDesc value) { EnsureScanDefinedMethods(); @@ -190,6 +196,7 @@ private bool SlowTryGetSetItem(out ComMethodDesc value) return true; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal bool TryGetMemberMethod(string name, out ComMethodDesc method) { EnsureScanDefinedMethods(); @@ -202,6 +209,7 @@ internal bool TryGetMemberEvent(string name, out ComEventDesc @event) return _comTypeDesc.TryGetEvent(name, out @event); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal bool TryGetMemberMethodExplicit(string name, out ComMethodDesc method) { EnsureScanDefinedMethods(); @@ -225,6 +233,7 @@ internal bool TryGetMemberMethodExplicit(string name, out ComMethodDesc method) throw Error.CouldNotGetDispId(name, string.Format(CultureInfo.InvariantCulture, "0x{0:X})", hresult)); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal bool TryGetPropertySetterExplicit(string name, out ComMethodDesc method, Type limitType, bool holdsNull) { EnsureScanDefinedMethods(); @@ -261,6 +270,7 @@ internal bool TryGetPropertySetterExplicit(string name, out ComMethodDesc method throw Error.CouldNotGetDispId(name, string.Format(CultureInfo.InvariantCulture, "0x{0:X})", hresult)); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal override IList GetMemberNames(bool dataOnly) { EnsureScanDefinedMethods(); @@ -269,6 +279,7 @@ internal override IList GetMemberNames(bool dataOnly) return ComTypeDesc.GetMemberNames(dataOnly); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal override IList> GetMembers(IEnumerable names) { if (names == null) @@ -312,6 +323,8 @@ internal override IList> GetMembers(IEnumerable>.Create(SplatInvokeBinder.s_instance); + _site = CallSite>.Create(SplatInvokeBinder.Instance); } return _site.Target(_site, _callable, args); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/StringArgBuilder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/StringArgBuilder.cs index 2eaace50234056..b4654e0c72c9f0 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/StringArgBuilder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/StringArgBuilder.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Runtime.InteropServices; @@ -19,6 +20,7 @@ internal StringArgBuilder(Type parameterType) _isWrapper = parameterType == typeof(BStrWrapper); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal override Expression Marshal(Expression parameter) { parameter = base.Marshal(parameter); @@ -35,6 +37,7 @@ internal override Expression Marshal(Expression parameter) return parameter; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal override Expression MarshalToRef(Expression parameter) { parameter = Marshal(parameter); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/TypeUtils.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/TypeUtils.cs index c5221aa6d36c96..6bd081d3784a64 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/TypeUtils.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/TypeUtils.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics.CodeAnalysis; using System.Reflection; namespace Microsoft.CSharp.RuntimeBinder.ComInterop @@ -73,6 +74,7 @@ internal static bool IsImplicitlyConvertible(Type source, Type destination) IsImplicitBoxingConversion(source, destination); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal static bool IsImplicitlyConvertible(Type source, Type destination, bool considerUserDefined) { return IsImplicitlyConvertible(source, destination) || @@ -80,6 +82,7 @@ internal static bool IsImplicitlyConvertible(Type source, Type destination, bool } //CONFORMING + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal static MethodInfo GetUserDefinedCoercionMethod(Type convertFrom, Type convertToType, bool implicitOnly) { // check for implicit coercions first diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/UnknownArgBuilder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/UnknownArgBuilder.cs index 9c0dd4c5c7bcc9..b0c19d50f299b0 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/UnknownArgBuilder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/UnknownArgBuilder.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Runtime.InteropServices; @@ -17,6 +18,7 @@ internal UnknownArgBuilder(Type parameterType) _isWrapper = parameterType == typeof(UnknownWrapper); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal override Expression Marshal(Expression parameter) { parameter = base.Marshal(parameter); @@ -33,6 +35,7 @@ internal override Expression Marshal(Expression parameter) return Helpers.Convert(parameter, typeof(object)); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal override Expression MarshalToRef(Expression parameter) { parameter = Marshal(parameter); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/VarEnumSelector.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/VarEnumSelector.cs index 40d36cae21e7f3..6e6535371c82e1 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/VarEnumSelector.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/VarEnumSelector.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.InteropServices; @@ -30,6 +31,7 @@ internal sealed class VarEnumSelector private static readonly Dictionary s_comToManagedPrimitiveTypes = CreateComToManagedPrimitiveTypes(); private static readonly IList> s_comPrimitiveTypeFamilies = CreateComPrimitiveTypeFamilies(); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal VarEnumSelector(Type[] explicitArgTypes) { VariantBuilders = new VariantBuilder[explicitArgTypes.Length]; @@ -141,6 +143,7 @@ private static IList> CreateComPrimitiveTypeFamilies() /// /// Get the (one representative type for each) primitive type families that the argument can be converted to /// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static List GetConversionsToComPrimitiveTypeFamilies(Type argumentType) { List compatibleComTypes = new List(); @@ -272,6 +275,7 @@ private static bool TryGetPrimitiveComType(Type argumentType, out VarEnum primit /// /// Is there a unique primitive type that has the best conversion for the argument /// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static bool TryGetPrimitiveComTypeViaConversion(Type argumentType, out VarEnum primitiveVarEnum) { // Look for a unique type family that the argument can be converted to. @@ -373,6 +377,7 @@ private VarEnum GetComType(ref Type argumentType) /// /// Get the COM Variant type that argument should be marshaled as for a call to COM /// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private VariantBuilder GetVariantBuilder(Type argumentType) { //argumentType is coming from MarshalType, null means the dynamic object holds @@ -419,6 +424,7 @@ private VariantBuilder GetVariantBuilder(Type argumentType) // This helper is called when we are looking for a ByVal marshalling // In a ByVal case we can take into account conversions or IConvertible if all other // attempts to find marshalling type failed + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static ArgBuilder GetByValArgBuilder(Type elementType, ref VarEnum elementVarEnum) { // If VT indicates that marshalling type is unknown. diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/VariantArgBuilder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/VariantArgBuilder.cs index de0493a771c56a..d2b5b149fe1663 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/VariantArgBuilder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/VariantArgBuilder.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Reflection; using System.Runtime.InteropServices; @@ -18,6 +19,7 @@ internal VariantArgBuilder(Type parameterType) _isWrapper = parameterType == typeof(VariantWrapper); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal override Expression Marshal(Expression parameter) { parameter = base.Marshal(parameter); @@ -34,6 +36,7 @@ internal override Expression Marshal(Expression parameter) return Helpers.Convert(parameter, typeof(object)); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal override Expression MarshalToRef(Expression parameter) { parameter = Marshal(parameter); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/VariantArray.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/VariantArray.cs index 620cc299c083c3..289a0531458459 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/VariantArray.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/VariantArray.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq.Expressions; using System.Reflection; @@ -49,11 +50,19 @@ internal static class VariantArray // (guarenteed less than 28, in practice 0-2) private static readonly List s_generatedTypes = new List(0); + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicFields, typeof(VariantArray1))] + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicFields, typeof(VariantArray2))] + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicFields, typeof(VariantArray4))] + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicFields, typeof(VariantArray8))] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "Types are either dynamically created or have dynamic dependency.")] internal static MemberExpression GetStructField(ParameterExpression variantArray, int field) { return Expression.Field(variantArray, "Element" + field); } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2055:UnrecognizedReflectionPattern", + Justification = "MakeGenericType is called on a dynamically created type that doesn't contain trimming annotations.")] internal static Type GetStructType(int args) { Debug.Assert(args >= 0); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/VariantBuilder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/VariantBuilder.cs index 5d3da6f009c12f..4fdb65bbd1aefb 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/VariantBuilder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/VariantBuilder.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Runtime.InteropServices; @@ -28,6 +29,7 @@ internal bool IsByRef get { return (_targetComType & VarEnum.VT_BYREF) != 0; } } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal Expression InitializeArgumentVariant(MemberExpression variant, Expression parameter) { //NOTE: we must remember our variant diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/DynamicDebuggerProxy.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/DynamicDebuggerProxy.cs index 5f0438a8ecc41d..c04fa1735478e2 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/DynamicDebuggerProxy.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/DynamicDebuggerProxy.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Dynamic; using System.Linq; using System.Linq.Expressions; @@ -79,6 +80,7 @@ public DynamicProperty(string name, object value) [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.RootHidden)] internal DynamicProperty[] Items { + [RequiresUnreferencedCode(Binder.TrimmerWarning)] get { if (results == null || results.Count == 0) @@ -106,6 +108,7 @@ public DynamicMetaObjectProviderDebugView(object arg) [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)] private static readonly ParameterExpression parameter = Expression.Parameter(typeof(object), "debug"); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static object TryEvalBinaryOperators( T1 arg1, T2 arg2, @@ -128,6 +131,7 @@ public static object TryEvalBinaryOperators( return site.Target(site, arg1, arg2); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static object TryEvalUnaryOperators(T obj, ExpressionType oper, Type accessibilityContext) { if (oper == ExpressionType.IsTrue || oper == ExpressionType.IsFalse) @@ -148,6 +152,7 @@ public static object TryEvalUnaryOperators(T obj, ExpressionType oper, Type a return site.Target(site, obj); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static K TryEvalCast(T obj, Type type, CSharpBinderFlags kind, Type accessibilityContext) { var site = CallSite>.Create(Binder.Convert(kind, type, accessibilityContext)); @@ -198,6 +203,7 @@ private static void CreateDelegateSignatureAndArgumentInfos( /// Creates a delegate based on type array that describe its signature and invokes it. /// /// Result of invoking the delegate. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static object CreateDelegateAndInvoke(Type[] delegateSignatureTypes, CallSiteBinder binder, object[] args) { Type delegateType = Expression.GetDelegateType(delegateSignatureTypes); @@ -223,6 +229,7 @@ private static object CreateDelegateAndInvoke(Type[] delegateSignatureTypes, Cal /// Type that determines context in which method should be called. /// Generic type arguments if there are any. /// Result of method invocation. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static object TryEvalMethodVarArgs( object[] methodArgs, Type[] argTypes, @@ -273,6 +280,7 @@ public static object TryEvalMethodVarArgs( /// Type that determines context in which method should be called. /// Determines if COM binder should return a callable object. /// Result of property invocation. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static object TryGetMemberValue(T obj, string propName, Type accessibilityContext, bool isResultIndexed) { // In most cases it's ok to use CSharpArgumentInfoFlags.None since target of property call is dynamic. @@ -300,6 +308,7 @@ public static object TryGetMemberValue(T obj, string propName, Type accessibi /// Flags describing each argument. /// Type that determines context in which method should be called. /// Result of property invocation. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static object TryGetMemberValueVarArgs( object[] propArgs, Type[] argTypes, @@ -333,6 +342,7 @@ public static object TryGetMemberValueVarArgs( /// /// Type that determines context in which method should be called. /// Result of property invocation. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static object TrySetMemberValue( TObject obj, string propName, @@ -364,6 +374,7 @@ public static object TrySetMemberValue( /// Flags describing each argument. /// Type that determines context in which method should be called. /// Result of property invocation. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static object TrySetMemberValueVarArgs( object[] propArgs, Type[] argTypes, @@ -417,9 +428,10 @@ internal static object TryGetMemberValue(object obj, string name, bool ignoreExc #if ENABLECOMBINDER [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)] - private static readonly Type ComObjectType = typeof(object).Assembly.GetType("System.__ComObject"); + private static readonly Type ComObjectType = Type.GetType("System.__ComObject, System.Private.CoreLib"); #endif + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static IList> QueryDynamicObject(object obj) { IDynamicMetaObjectProvider ido = obj as IDynamicMetaObjectProvider; diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ExpressionTreeCallRewriter.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ExpressionTreeCallRewriter.cs index 380d9d1e4053b5..b9f3fab762f382 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ExpressionTreeCallRewriter.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ExpressionTreeCallRewriter.cs @@ -42,6 +42,7 @@ private ExpressionTreeCallRewriter(Expression[] listOfParameters) ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static Expression Rewrite(ExprBinOp binOp, Expression[] listOfParameters) { ExpressionTreeCallRewriter rewriter = new ExpressionTreeCallRewriter(listOfParameters); @@ -67,6 +68,7 @@ public static Expression Rewrite(ExprBinOp binOp, Expression[] listOfParameters) ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected override Expr VisitSAVE(ExprBinOp pExpr) { // Saves should have a LHS that is a CALL to PM_EXPRESSION_PARAMETER @@ -83,6 +85,7 @@ protected override Expr VisitSAVE(ExprBinOp pExpr) ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected override Expr VisitCALL(ExprCall pExpr) { if (pExpr.PredefinedMethod == PREDEFMETH.PM_COUNT) @@ -209,11 +212,13 @@ protected override Expr VisitCALL(ExprCall pExpr) // ExpressionTreeRewriter has optimized away identity or up-cast conversions, leaving us with a bare parameter // access. Just get the expression for that parameter so the lambda produced can be p0 => p0 + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected override Expr VisitWRAP(ExprWrap pExpr) => new ExpressionExpr(GetExpression(pExpr)); #region Generators ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expr GenerateLambda(ExprCall pExpr) { // We always call Lambda(body, arrayinit) where the arrayinit @@ -235,6 +240,7 @@ private Expr GenerateLambda(ExprCall pExpr) ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expression GenerateCall(ExprCall pExpr) { // Our arguments are: object, methodinfo, parameters. @@ -278,6 +284,7 @@ private Expression GenerateCall(ExprCall pExpr) ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expression GenerateArrayIndex(ExprCall pExpr) { // We have two possibilities here - we're either a single index array, in which @@ -304,6 +311,7 @@ private Expression GenerateArrayIndex(ExprCall pExpr) ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expression GenerateConvert(ExprCall pExpr) { PREDEFMETH pm = pExpr.PredefinedMethod; @@ -373,6 +381,7 @@ private Expression GenerateConvert(ExprCall pExpr) ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expression GenerateProperty(ExprCall pExpr) { ExprList list = (ExprList)pExpr.OptionalArguments; @@ -410,6 +419,7 @@ private Expression GenerateProperty(ExprCall pExpr) ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expression GenerateField(ExprCall pExpr) { ExprList list = (ExprList)pExpr.OptionalArguments; @@ -437,6 +447,7 @@ private Expression GenerateField(ExprCall pExpr) ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expression GenerateInvoke(ExprCall pExpr) { ExprList list = (ExprList)pExpr.OptionalArguments; @@ -448,6 +459,7 @@ private Expression GenerateInvoke(ExprCall pExpr) ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expression GenerateNew(ExprCall pExpr) { ExprList list = (ExprList)pExpr.OptionalArguments; @@ -459,6 +471,7 @@ private Expression GenerateNew(ExprCall pExpr) ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static Expression GenerateConstantType(ExprCall pExpr) { ExprList list = (ExprList)pExpr.OptionalArguments; @@ -469,6 +482,7 @@ private static Expression GenerateConstantType(ExprCall pExpr) ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expression GenerateAssignment(ExprCall pExpr) { ExprList list = (ExprList)pExpr.OptionalArguments; @@ -480,6 +494,7 @@ private Expression GenerateAssignment(ExprCall pExpr) ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expression GenerateBinaryOperator(ExprCall pExpr) { ExprList list = (ExprList)pExpr.OptionalArguments; @@ -542,6 +557,7 @@ private Expression GenerateBinaryOperator(ExprCall pExpr) ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expression GenerateUserDefinedBinaryOperator(ExprCall pExpr) { ExprList list = (ExprList)pExpr.OptionalArguments; @@ -618,6 +634,7 @@ private Expression GenerateUserDefinedBinaryOperator(ExprCall pExpr) ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expression GenerateUnaryOperator(ExprCall pExpr) { PREDEFMETH pm = pExpr.PredefinedMethod; @@ -642,6 +659,7 @@ private Expression GenerateUnaryOperator(ExprCall pExpr) ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expression GenerateUserDefinedUnaryOperator(ExprCall pExpr) { PREDEFMETH pm = pExpr.PredefinedMethod; @@ -673,6 +691,7 @@ private Expression GenerateUserDefinedUnaryOperator(ExprCall pExpr) #region Helpers ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expression GetExpression(Expr pExpr) { if (pExpr is ExprWrap wrap) @@ -865,6 +884,7 @@ private Expression GetExpression(Expr pExpr) } } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expression[] GetArgumentsFromArrayInit(ExprArrayInit arrinit) { List expressions = new List(); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ICSharpBinder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ICSharpBinder.cs index dd08d83583fab9..3df4472e94bf94 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ICSharpBinder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ICSharpBinder.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics.CodeAnalysis; using Microsoft.CSharp.RuntimeBinder.Semantics; namespace Microsoft.CSharp.RuntimeBinder @@ -16,8 +17,10 @@ internal interface ICSharpBinder // are only dispatched dynamically when the receiver is dynamic, and hence boxed. bool IsBinderThatCanHaveRefReceiver { get; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] void PopulateSymbolTableWithName(Type callingType, ArgumentObject[] arguments); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] Expr DispatchPayload(RuntimeBinder runtimeBinder, ArgumentObject[] arguments, LocalVariableSymbol[] locals); BindingFlag BindingFlags { get; } diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/RuntimeBinder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/RuntimeBinder.cs index 5ddca856693125..ffc141a89d4f73 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/RuntimeBinder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/RuntimeBinder.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Dynamic; using System.Linq.Expressions; using Microsoft.CSharp.RuntimeBinder.Errors; @@ -20,6 +21,7 @@ internal readonly struct RuntimeBinder internal bool IsChecked => _binder.Context.Checked; + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public RuntimeBinder(Type contextType, bool isChecked = false) { AggregateSymbol context; @@ -38,6 +40,7 @@ public RuntimeBinder(Type contextType, bool isChecked = false) _binder = new ExpressionBinder(new BindingContext(context, isChecked)); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public Expression Bind(ICSharpBinder payload, Expression[] parameters, DynamicMetaObject[] args, out DynamicMetaObject deferredBinding) { // The lock is here to protect this instance of the binder from itself @@ -63,6 +66,7 @@ public Expression Bind(ICSharpBinder payload, Expression[] parameters, DynamicMe } } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expression BindCore( ICSharpBinder payload, Expression[] parameters, @@ -125,6 +129,7 @@ internal static void EnsureLockIsTaken() Debug.Assert(System.Threading.Monitor.IsEntered(s_bindLock)); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool DeferBinding( ICSharpBinder payload, ArgumentObject[] arguments, @@ -180,6 +185,7 @@ private bool DeferBinding( return false; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static Expression CreateExpressionTreeFromResult(Expression[] parameters, Scope pScope, Expr pResult) { // (3) - Place the result in a return statement and create the ExprBoundLambda. @@ -193,6 +199,7 @@ private static Expression CreateExpressionTreeFromResult(Expression[] parameters return e; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Type GetArgumentType(ICSharpBinder p, CSharpArgumentInfo argInfo, Expression param, DynamicMetaObject arg, int index) { Type t = argInfo.UseCompileTimeType ? param.Type : arg.LimitType; @@ -232,6 +239,7 @@ private Type GetArgumentType(ICSharpBinder p, CSharpArgumentInfo argInfo, Expres ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private ArgumentObject[] CreateArgumentArray( ICSharpBinder payload, Expression[] parameters, @@ -254,6 +262,7 @@ private ArgumentObject[] CreateArgumentArray( ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal static void PopulateSymbolTableWithPayloadInformation( ICSharpInvokeOrInvokeMemberBinder callOrInvoke, Type callingType, ArgumentObject[] arguments) { @@ -292,6 +301,7 @@ internal static void PopulateSymbolTableWithPayloadInformation( ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static void AddConversionsForArguments(ArgumentObject[] arguments) { foreach (ArgumentObject arg in arguments) @@ -302,6 +312,7 @@ private static void AddConversionsForArguments(ArgumentObject[] arguments) ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal ExprWithArgs DispatchPayload(ICSharpInvokeOrInvokeMemberBinder payload, ArgumentObject[] arguments, LocalVariableSymbol[] locals) => BindCall(payload, CreateCallingObjectForCall(payload, arguments, locals), arguments, locals); @@ -312,6 +323,7 @@ internal ExprWithArgs DispatchPayload(ICSharpInvokeOrInvokeMemberBinder payload, // we have a call off of a struct for example. If thats the case, don't treat the // local as a ref type. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static LocalVariableSymbol[] PopulateLocalScope( ICSharpBinder payload, Scope pScope, @@ -355,6 +367,7 @@ private static LocalVariableSymbol[] PopulateLocalScope( ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static ExprBoundLambda GenerateBoundLambda(Scope pScope, Expr call) { // We don't actually need the real delegate type here - we just need SOME delegate type. @@ -367,6 +380,7 @@ private static ExprBoundLambda GenerateBoundLambda(Scope pScope, Expr call) ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expr CreateLocal(Type type, bool isOut, LocalVariableSymbol local) { CType ctype; @@ -393,6 +407,7 @@ private Expr CreateLocal(Type type, bool isOut, LocalVariableSymbol local) ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal Expr CreateArgumentListEXPR( ArgumentObject[] arguments, LocalVariableSymbol[] locals, @@ -426,6 +441,7 @@ internal Expr CreateArgumentListEXPR( ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expr CreateArgumentEXPR(ArgumentObject argument, LocalVariableSymbol local) { Expr arg; @@ -495,6 +511,7 @@ private Expr CreateArgumentEXPR(ArgumentObject argument, LocalVariableSymbol loc ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static ExprMemberGroup CreateMemberGroupExpr( string Name, Type[] typeArguments, @@ -597,6 +614,7 @@ private static ExprMemberGroup CreateMemberGroupExpr( ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expr CreateProperty( SymWithType swt, Expr callingObject, @@ -616,6 +634,7 @@ private Expr CreateProperty( ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private ExprWithArgs CreateIndexer(SymWithType swt, Expr callingObject, Expr arguments, BindingFlag bindFlags) { IndexerSymbol index = swt.Sym as IndexerSymbol; @@ -627,6 +646,7 @@ private ExprWithArgs CreateIndexer(SymWithType swt, Expr callingObject, Expr arg ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expr CreateArray(Expr callingObject, Expr optionalIndexerArguments) { return _binder.BindArrayIndexCore(callingObject, optionalIndexerArguments); @@ -634,6 +654,7 @@ private Expr CreateArray(Expr callingObject, Expr optionalIndexerArguments) ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expr CreateField( SymWithType swt, Expr callingObject) @@ -657,6 +678,7 @@ private Expr CreateField( #region Calls ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expr CreateCallingObjectForCall( ICSharpInvokeOrInvokeMemberBinder payload, ArgumentObject[] arguments, @@ -695,6 +717,7 @@ private Expr CreateCallingObjectForCall( ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private ExprWithArgs BindCall( ICSharpInvokeOrInvokeMemberBinder payload, Expr callingObject, @@ -812,6 +835,7 @@ private static void CheckForConditionalMethodError(ExprCall call) } } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private void ReorderArgumentsForNamedAndOptional(Expr callingObject, ExprWithArgs result) { Expr arguments = result.OptionalArguments; @@ -912,6 +936,7 @@ private Expr StripNamedArguments(Expr pArg) #region UnaryOperators ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal Expr BindUnaryOperation( CSharpUnaryOperationBinder payload, ArgumentObject[] arguments, @@ -951,6 +976,7 @@ internal Expr BindUnaryOperation( ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal Expr BindBinaryOperation( CSharpBinaryOperationBinder payload, ArgumentObject[] arguments, @@ -1073,6 +1099,7 @@ private static OperatorKind GetOperatorKind(ExpressionType p, bool bIsLogical) #region Properties ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal Expr BindProperty( ICSharpBinder payload, ArgumentObject argument, @@ -1156,6 +1183,7 @@ internal Expr BindProperty( #region Casts ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal Expr BindImplicitConversion( ArgumentObject[] arguments, Type returnType, @@ -1194,6 +1222,7 @@ internal Expr BindImplicitConversion( ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal Expr BindExplicitConversion(ArgumentObject[] arguments, Type returnType, LocalVariableSymbol[] locals) { Debug.Assert(arguments.Length == 1); @@ -1213,6 +1242,7 @@ internal Expr BindExplicitConversion(ArgumentObject[] arguments, Type returnType ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal Expr BindAssignment( ICSharpBinder payload, ArgumentObject[] arguments, @@ -1252,6 +1282,7 @@ internal Expr BindAssignment( #region Events ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal Expr BindIsEvent( CSharpIsEventBinder binder, ArgumentObject[] arguments, diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/RuntimeBinderExtensions.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/RuntimeBinderExtensions.cs index 1323e2c24bb29f..27731058c76c7c 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/RuntimeBinderExtensions.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/RuntimeBinderExtensions.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Reflection; @@ -315,6 +316,7 @@ public static bool HasSameMetadataDefinitionAs(this MemberInfo mi1, MemberInfo m return mi1.Module.Equals(mi2.Module) && s_MemberEquivalence(mi1, mi2); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static string GetIndexerName(this Type type) { Debug.Assert(type != null); @@ -334,6 +336,7 @@ public static string GetIndexerName(this Type type) return name; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static string GetTypeIndexerName(Type type) { Debug.Assert(type != null); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/BinOpSig.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/BinOpSig.cs index 9874eaef5647c2..2f9825d586c27b 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/BinOpSig.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/BinOpSig.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Microsoft.CSharp.RuntimeBinder.Syntax; namespace Microsoft.CSharp.RuntimeBinder.Semantics @@ -74,6 +75,7 @@ public BinOpFullSig(CType type1, CType type2, PfnBindBinOp pfn, OpSigFlags grfos Set the values of the BinOpFullSig from the given BinOpSig. The ExpressionBinder is needed to get the predefined types. Returns true iff the predef types are found. ***************************************************************************************************/ + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public BinOpFullSig(ExpressionBinder fnc, BinOpSig bos) { this.pt1 = bos.pt1; diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Binding/Better.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Binding/Better.cs index 22e754f4e00ca1..c064a0f4170d82 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Binding/Better.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Binding/Better.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Microsoft.CSharp.RuntimeBinder.Syntax; namespace Microsoft.CSharp.RuntimeBinder.Semantics @@ -275,6 +276,7 @@ private static TypeArray RearrangeNamedArguments(TypeArray pta, MethPropWithInst // // Returns Left if m1 is better, Right if m2 is better, or Neither/Same + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private BetterType WhichMethodIsBetter( CandidateFunctionMember node1, CandidateFunctionMember node2, @@ -392,6 +394,7 @@ private BetterType WhichMethodIsBetter( return betterMethod; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private BetterType WhichConversionIsBetter(CType argType, CType p1, CType p2) { Debug.Assert(argType != null); @@ -475,6 +478,7 @@ private BetterType WhichConversionIsBetter(CType argType, CType p1, CType p2) //////////////////////////////////////////////////////////////////////////////// // Determine best method for overload resolution. Returns null if no best // method, in which case two tying methods are returned for error reporting. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private CandidateFunctionMember FindBestMethod( List list, CType pTypeThrough, diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Conversion.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Conversion.cs index cdee4bc4154f92..cb2dd20c9bce04 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Conversion.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Conversion.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using Microsoft.CSharp.RuntimeBinder.Errors; using Microsoft.CSharp.RuntimeBinder.Syntax; @@ -208,6 +209,8 @@ private static bool isUserDefinedConversion(PredefinedType ptSrc, PredefinedType }; #if DEBUG private static volatile bool s_fCheckedBetter; + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private void CheckBetterTable() { if (s_fCheckedBetter) @@ -235,6 +238,7 @@ private void CheckBetterTable() } #endif // DEBUG + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private BetterType WhichSimpleConversionIsBetter(PredefinedType pt1, PredefinedType pt2) { #if DEBUG @@ -252,6 +256,7 @@ Determined which conversion to a predefined type is better relative to a given t assumed that the given type is implicitly convertible to both of the predefined types (possibly via a user defined conversion, method group conversion, etc). ***************************************************************************************************/ + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private BetterType WhichTypeIsBetter(PredefinedType pt1, PredefinedType pt2, CType typeGiven) { if (pt1 == pt2) @@ -287,6 +292,7 @@ Determined which conversion is better relative to a given type. It is assumed th (or its associated expression) is implicitly convertible to both of the types (possibly via a user defined conversion, method group conversion, etc). ***************************************************************************************************/ + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private BetterType WhichTypeIsBetter(CType type1, CType type2, CType typeGiven) { Debug.Assert(type1 != null && type2 != null); @@ -329,20 +335,26 @@ private BetterType WhichTypeIsBetter(CType type1, CType type2, CType typeGiven) } // returns true if an implicit conversion exists from source type to dest type. flags is an optional parameter. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool canConvert(CType src, CType dest, CONVERTTYPE flags) => BindImplicitConversion(null, src, dest, flags); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public bool canConvert(CType src, CType dest) => canConvert(src, dest, 0); // returns true if a implicit conversion exists from source expr to dest type. flags is an optional parameter. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool canConvert(Expr expr, CType dest) => canConvert(expr, dest, 0); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool canConvert(Expr expr, CType dest, CONVERTTYPE flags) => BindImplicitConversion(expr, expr.Type, dest, flags); // performs an implicit conversion if it's possible. otherwise displays an error. flags is an optional parameter. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expr mustConvertCore(Expr expr, CType destExpr) => mustConvertCore(expr, destExpr, 0); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expr mustConvertCore(Expr expr, CType dest, CONVERTTYPE flags) { Debug.Assert(!(expr is ExprMemberGroup)); @@ -390,11 +402,13 @@ private Expr mustConvertCore(Expr expr, CType dest, CONVERTTYPE flags) // If the conversion is possible it will modify an Anonymous Method expr thus changing results of // future conversions. It will also produce possible binding errors for method groups. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public Expr tryConvert(Expr expr, CType dest) { return tryConvert(expr, dest, 0); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expr tryConvert(Expr expr, CType dest, CONVERTTYPE flags) { if (BindImplicitConversion(expr, expr.Type, dest, out Expr exprResult, flags)) @@ -408,8 +422,10 @@ private Expr tryConvert(Expr expr, CType dest, CONVERTTYPE flags) return null; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public Expr mustConvert(Expr expr, CType dest) => mustConvert(expr, dest, (CONVERTTYPE)0); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expr mustConvert(Expr expr, CType dest, CONVERTTYPE flags) => mustConvertCore(expr, dest, flags); // public bool canCast(Expr expr, CType dest) @@ -419,6 +435,7 @@ private Expr tryConvert(Expr expr, CType dest, CONVERTTYPE flags) // } // performs an explicit conversion if its possible. otherwise displays an error. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expr mustCastCore(Expr expr, CType dest, CONVERTTYPE flags) { Debug.Assert(!(expr is ExprMemberGroup)); @@ -501,21 +518,28 @@ private static RuntimeBinderException CantConvert(Expr expr, CType dest) return ErrorHandling.Error(ErrorCode.ERR_NoExplicitConv, new ErrArg(expr.Type, ErrArgFlags.Unique), new ErrArg(dest, ErrArgFlags.Unique)); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public Expr mustCast(Expr expr, CType dest) => mustCast(expr, dest, 0); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public Expr mustCast(Expr expr, CType dest, CONVERTTYPE flags) => mustCastCore(expr, dest, flags); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expr MustCastInUncheckedContext(Expr expr, CType dest, CONVERTTYPE flags) => new ExpressionBinder(new BindingContext(Context)).mustCast(expr, dest, flags); // returns true if an explicit conversion exists from source type to dest type. flags is an optional parameter. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool canCast(CType src, CType dest, CONVERTTYPE flags) => BindExplicitConversion(null, src, dest, flags); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool BindImplicitConversion(Expr pSourceExpr, CType pSourceType, CType destinationType, CONVERTTYPE flags) { ImplicitConversion binder = new ImplicitConversion(this, pSourceExpr, pSourceType, destinationType, false, flags); return binder.Bind(); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool BindImplicitConversion(Expr pSourceExpr, CType pSourceType, CType destinationType, out Expr ppDestinationExpr, CONVERTTYPE flags) { ImplicitConversion binder = new ImplicitConversion(this, pSourceExpr, pSourceType, destinationType, true, flags); @@ -524,6 +548,7 @@ private bool BindImplicitConversion(Expr pSourceExpr, CType pSourceType, CType d return result; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool BindImplicitConversion(Expr pSourceExpr, CType pSourceType, CType destinationType, bool needsExprDest, out Expr ppDestinationExpr, CONVERTTYPE flags) { ImplicitConversion binder = new ImplicitConversion(this, pSourceExpr, pSourceType, destinationType, needsExprDest, flags); @@ -532,6 +557,7 @@ private bool BindImplicitConversion(Expr pSourceExpr, CType pSourceType, CType d return result; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool BindExplicitConversion(Expr pSourceExpr, CType pSourceType, CType destinationType, bool needsExprDest, out Expr ppDestinationExpr, CONVERTTYPE flags) { ExplicitConversion binder = new ExplicitConversion(this, pSourceExpr, pSourceType, destinationType, needsExprDest, flags); @@ -540,6 +566,7 @@ private bool BindExplicitConversion(Expr pSourceExpr, CType pSourceType, CType d return result; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool BindExplicitConversion(Expr pSourceExpr, CType pSourceType, CType destinationType, out Expr ppDestinationExpr, CONVERTTYPE flags) { ExplicitConversion binder = new ExplicitConversion(this, pSourceExpr, pSourceType, destinationType, true, flags); @@ -548,6 +575,7 @@ private bool BindExplicitConversion(Expr pSourceExpr, CType pSourceType, CType d return result; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool BindExplicitConversion(Expr pSourceExpr, CType pSourceType, CType destinationType, CONVERTTYPE flags) { ExplicitConversion binder = new ExplicitConversion(this, pSourceExpr, pSourceType, destinationType, false, flags); @@ -594,6 +622,7 @@ or output of the conversion is not a nullable. A conversion that needs fewer lifts is better than one that requires more (if the lifted forms have identical signatures). ***************************************************************************************************/ + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool bindUserDefinedConversion(Expr exprSrc, CType typeSrc, CType typeDst, bool needExprDest, out Expr pexprDst, bool fImplicitOnly) { pexprDst = null; @@ -1060,12 +1089,14 @@ private static void MarkAsIntermediateConversion(Expr pExpr) } } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expr BindUDConversionCore(Expr pFrom, CType pTypeFrom, CType pTypeTo, CType pTypeDestination, MethWithInst mwiBest) { Expr ppTransformedArgument; return BindUDConversionCore(pFrom, pTypeFrom, pTypeTo, pTypeDestination, mwiBest, out ppTransformedArgument); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expr BindUDConversionCore(Expr pFrom, CType pTypeFrom, CType pTypeTo, CType pTypeDestination, MethWithInst mwiBest, out Expr ppTransformedArgument) { Expr pTransformedArgument = mustCastCore(pFrom, pTypeFrom, CONVERTTYPE.NOUDC); @@ -1081,6 +1112,7 @@ private Expr BindUDConversionCore(Expr pFrom, CType pTypeFrom, CType pTypeTo, CT /* * Fold a constant cast. Returns true if the constant could be folded. */ + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private ConstCastResult bindConstantCast(Expr exprSrc, CType typeDest, bool needExprDest, out Expr pexprDest, bool explicitConversion) { pexprDest = null; @@ -1291,6 +1323,7 @@ is closer. The return value is -1 if type1 is closer, +1 if type2 is closer and 0 if neither is closer. ***************************************************************************************************/ + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private int CompareSrcTypesBased(CType type1, bool fImplicit1, CType type2, bool fImplicit2) { Debug.Assert(type1 != type2); @@ -1324,6 +1357,7 @@ is closer. The return value is -1 if type1 is closer, +1 if type2 is closer and 0 if neither is closer. ***************************************************************************************************/ + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private int CompareDstTypesBased(CType type1, bool fImplicit1, CType type2, bool fImplicit2) { Debug.Assert(type1 != type2); @@ -1335,9 +1369,11 @@ private int CompareDstTypesBased(CType type1, bool fImplicit1, CType type2, bool return 0; return (fImplicit1 == fCon1) ? +1 : -1; } + /* * Bind a constant cast to or from decimal. Return null if cast can't be done. */ + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static Expr BindDecimalConstCast(CType destType, CType srcType, ExprConstant src) { CType typeDecimal = SymbolLoader.GetPredefindType(PredefinedType.PT_DECIMAL); @@ -1447,6 +1483,7 @@ private static Expr BindDecimalConstCast(CType destType, CType srcType, ExprCons return null; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool CanExplicitConversionBeBoundInUncheckedContext(Expr exprSrc, CType typeSrc, CType typeDest, CONVERTTYPE flags) { Debug.Assert(typeDest != null); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Conversions.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Conversions.cs index 670bda77d40f8c..8e6519fbfffca3 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Conversions.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Conversions.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Microsoft.CSharp.RuntimeBinder.Syntax; namespace Microsoft.CSharp.RuntimeBinder.Semantics @@ -23,6 +24,7 @@ Determine whether there is an implicit reference conversion from typeSrc to type when the source is a reference type and the destination is a base type of the source. Note that typeDst.IsRefType() may still return false (when both are type parameters). ***************************************************************************************************/ + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static bool FImpRefConv(CType typeSrc, CType typeDst) => typeSrc.IsReferenceType && SymbolLoader.HasIdentityOrImplicitReferenceConversion(typeSrc, typeDst); @@ -70,6 +72,7 @@ src to dst. The latter two cases can happen with type variables even though the other type variable is not a reference type. ***************************************************************************************************/ + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static bool FExpRefConv(CType typeSrc, CType typeDst) { Debug.Assert(typeSrc != null); @@ -215,9 +218,11 @@ o If type parameter Xi is declared to be covariant ("out") then Si must be conve o If type parameter Xi is declared to be contravariant ("in") then either Si must be identical to Ti, or Si and Ti must both be reference types. ***************************************************************************************************/ + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static bool HasGenericDelegateExplicitReferenceConversion(CType source, CType target) => target is AggregateType aggTarget && HasGenericDelegateExplicitReferenceConversion(source, aggTarget); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static bool HasGenericDelegateExplicitReferenceConversion(CType pSource, AggregateType pTarget) { if (!(pSource is AggregateType aggSrc) || diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ExplicitConversion.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ExplicitConversion.cs index f51d12b4e9a517..620206fb6d99fa 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ExplicitConversion.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ExplicitConversion.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Microsoft.CSharp.RuntimeBinder.Syntax; namespace Microsoft.CSharp.RuntimeBinder.Semantics @@ -65,6 +66,7 @@ public ExplicitConversion(ExpressionBinder binder, Expr exprSrc, CType typeSrc, * logic is only concerned with conversions that can be made explicitly, but * not implicitly. */ + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public bool Bind() { // To test for a standard conversion, call canConvert(exprSrc, typeDest, STANDARDANDCONVERTTYPE.NOUDC) and @@ -181,6 +183,7 @@ public bool Bind() return false; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool bindExplicitConversionFromNub() { Debug.Assert(_typeSrc != null); @@ -219,6 +222,7 @@ private bool bindExplicitConversionFromNub() return false; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool bindExplicitConversionFromArrayToIList() { // 13.2.2 @@ -261,6 +265,7 @@ private bool bindExplicitConversionFromArrayToIList() return true; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool bindExplicitConversionFromIListToArray(ArrayType arrayDest) { // 13.2.2 @@ -301,6 +306,7 @@ private bool bindExplicitConversionFromIListToArray(ArrayType arrayDest) return true; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool bindExplicitConversionFromArrayToArray(ArrayType arraySrc, ArrayType arrayDest) { // 13.2.2 @@ -333,6 +339,7 @@ private bool bindExplicitConversionFromArrayToArray(ArrayType arraySrc, ArrayTyp return false; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool bindExplicitConversionToArray(ArrayType arrayDest) { Debug.Assert(_typeSrc != null); @@ -363,6 +370,7 @@ private bool bindExplicitConversionToArray(ArrayType arrayDest) return false; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool bindExplicitConversionToPointer() { // 27.4 Pointer conversions @@ -398,6 +406,7 @@ private bool bindExplicitConversionToPointer() // participating enum-type as the underlying type of that enum-type, and then performing // an implicit or explicit numeric conversion between the resulting types. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private AggCastResult bindExplicitConversionFromEnumToAggregate(AggregateType aggTypeDest) { Debug.Assert(_typeSrc != null); @@ -440,6 +449,7 @@ private AggCastResult bindExplicitConversionFromEnumToAggregate(AggregateType ag return AggCastResult.Success; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private AggCastResult bindExplicitConversionFromDecimalToEnum(AggregateType aggTypeDest) { Debug.Assert(_typeSrc != null); @@ -481,6 +491,7 @@ private AggCastResult bindExplicitConversionFromDecimalToEnum(AggregateType aggT return bIsConversionOK ? AggCastResult.Success : AggCastResult.Failure; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private AggCastResult bindExplicitConversionFromEnumToDecimal(AggregateType aggTypeDest) { Debug.Assert(_typeSrc != null); @@ -534,6 +545,7 @@ private AggCastResult bindExplicitConversionFromEnumToDecimal(AggregateType aggT return AggCastResult.Success; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private AggCastResult bindExplicitConversionToEnum(AggregateType aggTypeDest) { Debug.Assert(_typeSrc != null); @@ -579,6 +591,7 @@ private AggCastResult bindExplicitConversionToEnum(AggregateType aggTypeDest) return AggCastResult.Failure; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private AggCastResult bindExplicitConversionBetweenSimpleTypes(AggregateType aggTypeDest) { // 13.2.1 @@ -647,6 +660,7 @@ private AggCastResult bindExplicitConversionBetweenSimpleTypes(AggregateType agg return bConversionOk ? AggCastResult.Success : AggCastResult.Failure; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private AggCastResult bindExplicitConversionBetweenAggregates(AggregateType aggTypeDest) { // 13.2.3 @@ -698,6 +712,7 @@ private AggCastResult bindExplicitConversionBetweenAggregates(AggregateType aggT return AggCastResult.Failure; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private AggCastResult bindExplicitConversionFromPointerToInt(AggregateType aggTypeDest) { // 27.4 Pointer conversions @@ -715,6 +730,7 @@ private AggCastResult bindExplicitConversionFromPointerToInt(AggregateType aggTy return AggCastResult.Success; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private AggCastResult bindExplicitConversionToAggregate(AggregateType aggTypeDest) { Debug.Assert(_typeSrc != null); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ExprFactory.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ExprFactory.cs index 1d83a5c20147a9..a9eb6ee419931b 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ExprFactory.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ExprFactory.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Microsoft.CSharp.RuntimeBinder.Syntax; namespace Microsoft.CSharp.RuntimeBinder.Semantics @@ -32,46 +33,61 @@ public static ExprMemberGroup CreateMemGroup(Expr obj, MethPropWithInst method) obj, new CMemberLookupResults(TypeArray.Allocate((CType)method.GetType()), name)); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static ExprUserDefinedConversion CreateUserDefinedConversion(Expr arg, Expr call, MethWithInst method) => new ExprUserDefinedConversion(arg, call, method); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static ExprCast CreateCast(CType type, Expr argument) => CreateCast(0, type, argument); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static ExprCast CreateCast(EXPRFLAG flags, CType type, Expr argument) => new ExprCast(flags, type, argument); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static ExprLocal CreateLocal(LocalVariableSymbol local) => new ExprLocal(local); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static ExprBoundLambda CreateAnonymousMethod(AggregateType delegateType, Scope argumentScope, Expr expression) => new ExprBoundLambda(delegateType, argumentScope, expression); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static ExprMethodInfo CreateMethodInfo(MethPropWithInst mwi) => CreateMethodInfo(mwi.Meth(), mwi.GetType(), mwi.TypeArgs); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static ExprMethodInfo CreateMethodInfo(MethodSymbol method, AggregateType methodType, TypeArray methodParameters) => new ExprMethodInfo( TypeManager.GetPredefAgg(method.IsConstructor() ? PredefinedType.PT_CONSTRUCTORINFO : PredefinedType.PT_METHODINFO).getThisType(), method, methodType, methodParameters); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static ExprPropertyInfo CreatePropertyInfo(PropertySymbol prop, AggregateType propertyType) => new ExprPropertyInfo(TypeManager.GetPredefAgg(PredefinedType.PT_PROPERTYINFO).getThisType(), prop, propertyType); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static ExprFieldInfo CreateFieldInfo(FieldSymbol field, AggregateType fieldType) => new ExprFieldInfo(field, fieldType, TypeManager.GetPredefAgg(PredefinedType.PT_FIELDINFO).getThisType()); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static ExprTypeOf CreateTypeOf(CType sourceType) => new ExprTypeOf(TypeManager.GetPredefAgg(PredefinedType.PT_TYPE).getThisType(), sourceType); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static ExprUserLogicalOp CreateUserLogOp(CType type, Expr trueFalseCall, ExprCall operatorCall) => new ExprUserLogicalOp(type, trueFalseCall, operatorCall); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static ExprConcat CreateConcat(Expr first, Expr second) => new ExprConcat(first, second); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static ExprConstant CreateStringConstant(string str) => CreateConstant(TypeManager.GetPredefAgg(PredefinedType.PT_STRING).getThisType(), ConstVal.Get(str)); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static ExprMultiGet CreateMultiGet(EXPRFLAG flags, CType type, ExprMulti multi) => new ExprMultiGet(type, flags, multi); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static ExprMulti CreateMulti(EXPRFLAG flags, CType type, Expr left, Expr op) => new ExprMulti(type, flags, left, op); @@ -82,7 +98,7 @@ public static ExprMulti CreateMulti(EXPRFLAG flags, CType type, Expr left, Expr // type - Non-null // // This returns a null for reference types and an EXPRZEROINIT for all others. - + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static Expr CreateZeroInit(CType type) { Debug.Assert(type != null); @@ -122,9 +138,11 @@ public static Expr CreateZeroInit(CType type) public static ExprConstant CreateConstant(CType type, ConstVal constVal) => new ExprConstant(type, constVal); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static ExprConstant CreateIntegerConstant(int x) => CreateConstant(TypeManager.GetPredefAgg(PredefinedType.PT_INT).getThisType(), ConstVal.Get(x)); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static ExprConstant CreateBoolConstant(bool b) => CreateConstant(TypeManager.GetPredefAgg(PredefinedType.PT_BOOL).getThisType(), ConstVal.Get(b)); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ExpressionBinder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ExpressionBinder.cs index 9af97dddaf3ad2..f0a34542abef79 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ExpressionBinder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ExpressionBinder.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Microsoft.CSharp.RuntimeBinder.Errors; using Microsoft.CSharp.RuntimeBinder.Syntax; @@ -268,6 +269,7 @@ public ExpressionBinder(BindingContext context) Context = context; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static AggregateType GetPredefindType(PredefinedType pt) { Debug.Assert(pt != PredefinedType.PT_VOID); // use getVoidType() @@ -275,12 +277,14 @@ private static AggregateType GetPredefindType(PredefinedType pt) return SymbolLoader.GetPredefindType(pt); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expr GenerateAssignmentConversion(Expr op1, Expr op2, bool allowExplicit) => allowExplicit ? mustCastCore(op2, op1.Type, 0) : mustConvertCore(op2, op1.Type); //////////////////////////////////////////////////////////////////////////////// // Bind the simple assignment operator =. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public Expr BindAssignment(Expr op1, Expr op2, bool allowExplicit) { Debug.Assert(op1 is ExprCast @@ -295,6 +299,7 @@ public Expr BindAssignment(Expr op1, Expr op2, bool allowExplicit) return GenerateOptimizedAssignment(op1, op2); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal Expr BindArrayIndexCore(Expr pOp1, Expr pOp2) { CType pIntType = GetPredefindType(PredefinedType.PT_INT); @@ -311,10 +316,10 @@ internal Expr BindArrayIndexCore(Expr pOp1, Expr pOp2) Expr transformedIndices = pOp2.Map( x => { - Expr pTemp = binder.mustConvert(x, pDestType); + Expr pTemp = binder.MustConvertWithSuppressedMessage(x, pDestType); return pDestType == pIntType ? pTemp - : ExprFactory.CreateCast(EXPRFLAG.EXF_INDEXEXPR, pDestType, pTemp); + : ExprFactoryCreateCastWithSuppressedMessage(EXPRFLAG.EXF_INDEXEXPR, pDestType, pTemp); }); // Allocate a new expression, the type is the element type of the array. @@ -322,11 +327,23 @@ internal Expr BindArrayIndexCore(Expr pOp1, Expr pOp2) return ExprFactory.CreateArrayIndex(elementType, pOp1, transformedIndices); } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "Workarounds https://github.com/mono/linker/issues/1416. All usages are marked as unsafe.")] + private Expr MustConvertWithSuppressedMessage(Expr x, CType pDestType) + => mustConvert(x, pDestType); + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "Workarounds https://github.com/mono/linker/issues/1416. All usages are marked as unsafe.")] + private static ExprCast ExprFactoryCreateCastWithSuppressedMessage(EXPRFLAG flags, CType type, Expr argument) + => ExprFactory.CreateCast(flags, type, argument); + //////////////////////////////////////////////////////////////////////////////// // Create a cast node with the given expression flags. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private void bindSimpleCast(Expr exprSrc, CType typeDest, out Expr pexprDest) => bindSimpleCast(exprSrc, typeDest, out pexprDest, 0); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private void bindSimpleCast(Expr exprSrc, CType typeDest, out Expr pexprDest, EXPRFLAG exprFlags) { Debug.Assert(typeDest != null); @@ -369,6 +386,7 @@ private void bindSimpleCast(Expr exprSrc, CType typeDest, out Expr pexprDest, EX // args - arguments // exprFlags - Flags to put on the generated expr + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private ExprCall BindToMethod(MethWithInst mwi, Expr pArguments, ExprMemberGroup pMemGroup, MemLookFlags flags) { Debug.Assert(mwi.Sym is MethodSymbol && (!mwi.Meth().isOverride || mwi.Meth().isHideByName)); @@ -413,6 +431,7 @@ private ExprCall BindToMethod(MethWithInst mwi, Expr pArguments, ExprMemberGroup // Construct the Expr node which corresponds to a field expression // for a given field and pObject pointer. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal Expr BindToField(Expr pOptionalObject, FieldWithType fwt, BindingFlag bindFlags) { Debug.Assert(fwt.GetType() != null && fwt.Field().getClass() == fwt.GetType().OwningAggregate); @@ -438,6 +457,7 @@ internal Expr BindToField(Expr pOptionalObject, FieldWithType fwt, BindingFlag b //////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal ExprProperty BindToProperty(Expr pObject, PropWithType pwt, BindingFlag bindFlags, Expr args, ExprMemberGroup pMemGroup) { Debug.Assert(pwt.Sym is PropertySymbol && @@ -513,6 +533,7 @@ internal ExprProperty BindToProperty(Expr pObject, PropWithType pwt, BindingFlag return result; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal Expr bindUDUnop(ExpressionKind ek, Expr arg) { Name pName = ExpressionKindName(ek); @@ -627,6 +648,7 @@ internal Expr bindUDUnop(ExpressionKind ek, Expr arg) return ExprFactory.CreateUserDefinedUnaryOperator(ek, call.Type, arg, call, pmethBest.mpwi); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private ExprCall BindLiftedUDUnop(Expr arg, CType typeArg, MethPropWithInst mpwi) { CType typeRaw = typeArg.StripNubs(); @@ -655,6 +677,7 @@ private ExprCall BindLiftedUDUnop(Expr arg, CType typeArg, MethPropWithInst mpwi return call; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private ExprCall BindUDUnopCall(Expr arg, CType typeArg, MethPropWithInst mpwi) { CType typeRet = TypeManager.SubstType(mpwi.Meth().RetType, mpwi.GetType()); @@ -669,6 +692,7 @@ private ExprCall BindUDUnopCall(Expr arg, CType typeArg, MethPropWithInst mpwi) //////////////////////////////////////////////////////////////////////////////// // Given a method group or indexer group, bind it to the arguments for an // invocation. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private GroupToArgsBinderResult BindMethodGroupToArgumentsCore(BindingFlag bindFlags, ExprMemberGroup grp, Expr args, int carg, NamedArgumentsKind namedArgumentsKind) { ArgInfos pargInfo = new ArgInfos { carg = carg }; @@ -686,6 +710,7 @@ private GroupToArgsBinderResult BindMethodGroupToArgumentsCore(BindingFlag bindF //////////////////////////////////////////////////////////////////////////////// // Given a method group or indexer group, bind it to the arguments for an // invocation. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal ExprWithArgs BindMethodGroupToArguments(BindingFlag bindFlags, ExprMemberGroup grp, Expr args) { Debug.Assert(grp.SymKind == SYMKIND.SK_MethodSymbol || grp.SymKind == SYMKIND.SK_PropertySymbol && ((grp.Flags & EXPRFLAG.EXF_INDEXER) != 0)); @@ -792,6 +817,7 @@ private static ErrorCode GetStandardLvalueError(CheckLvalueKind kind) : ErrorCode.ERR_AssgLvalueExpected; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private void CheckLvalueProp(ExprProperty prop) { Debug.Assert(prop != null); @@ -808,6 +834,7 @@ private void CheckLvalueProp(ExprProperty prop) CheckPropertyAccess(prop.MethWithTypeSet, prop.PropWithTypeSlot, type); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private void CheckPropertyAccess(MethWithType mwt, PropWithType pwtSlot, CType type) { switch (CSemanticChecker.CheckAccess2(mwt.Meth(), mwt.GetType(), ContextForMemberLookup, type)) @@ -822,6 +849,7 @@ private void CheckPropertyAccess(MethWithType mwt, PropWithType pwtSlot, CType t //////////////////////////////////////////////////////////////////////////////// // A false return means not to process the expr any further - it's totally out // of place. For example - a method group or an anonymous method. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private void CheckLvalue(Expr expr, CheckLvalueKind kind) { if (expr.isLvalue()) @@ -913,6 +941,7 @@ private static void PostBindProperty(PropWithType pwt, out MethWithType pmwtGet, } } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expr AdjustMemberObject(SymWithType swt, Expr pObject) { // Assert that the type is present and is an instantiation of the member's parent. @@ -1029,6 +1058,7 @@ private static void AssertObjectIsLvalue(Expr pObject) ); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private void verifyMethodArgs(ExprWithArgs call, CType callingObjectType) { Debug.Assert(call != null); @@ -1041,6 +1071,7 @@ private void verifyMethodArgs(ExprWithArgs call, CType callingObjectType) call.OptionalArguments = newArgs; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private void AdjustCallArgumentsForParams(CType callingObjectType, CType type, MethodOrPropertySymbol mp, TypeArray pTypeArgs, Expr argsPtr, out Expr newArgs) { Debug.Assert(mp != null); @@ -1252,6 +1283,7 @@ private void AdjustCallArgumentsForParams(CType callingObjectType, CType type, M PredefinedType.PT_ULONG }; + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal CType ChooseArrayIndexType(Expr args) { // first, select the allowable types @@ -1373,11 +1405,13 @@ private static bool IsConvInTable(List convTable, MethodSymbol meth, //////////////////////////////////////////////////////////////////////////////// // Check to see if an integral constant is within range of a integral // destination type. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static bool isConstantInRange(ExprConstant exprSrc, CType typeDest) { return isConstantInRange(exprSrc, typeDest, false); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static bool isConstantInRange(ExprConstant exprSrc, CType typeDest, bool realsOk) { FUNDTYPE ftSrc = exprSrc.Type.FundamentalType; diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/GroupToArgsBinder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/GroupToArgsBinder.cs index 71412c94c56986..e637e236a7cc86 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/GroupToArgsBinder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/GroupToArgsBinder.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Microsoft.CSharp.RuntimeBinder.Errors; using Microsoft.CSharp.RuntimeBinder.Syntax; @@ -86,6 +87,7 @@ public GroupToArgsBinder(ExpressionBinder exprBinder, BindingFlag bindFlags, Exp // This method does the actual binding. // ---------------------------------------------------------------------------- + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public void Bind() { Debug.Assert(_pGroup.SymKind == SYMKIND.SK_MethodSymbol || _pGroup.SymKind == SYMKIND.SK_PropertySymbol && 0 != (_pGroup.Flags & EXPRFLAG.EXF_INDEXER)); @@ -106,6 +108,7 @@ private static CType GetTypeQualifier(ExprMemberGroup pGroup) return (pGroup.Flags & EXPRFLAG.EXF_CTOR) != 0 ? pGroup.ParentType : pGroup.OptionalObject?.Type; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private void LookForCandidates() { bool fExpanded = false; @@ -303,6 +306,7 @@ private static void CopyArgInfos(ArgInfos src, ArgInfos dst) } } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool GetResultOfBind() { // We looked at all the evidence, and we come to render the verdict: @@ -363,6 +367,7 @@ private bool GetResultOfBind() // we can find matching parameters for each named arguments, and all parameters // that do not have a matching argument are optional parameters. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool ReOrderArgsForNamedArguments() { // First we need to find the method that we're actually trying to call. @@ -391,6 +396,7 @@ private bool ReOrderArgsForNamedArguments() return _bArgumentsChangedForNamedOrOptionalArguments; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal static bool ReOrderArgsForNamedArguments( MethodOrPropertySymbol methprop, TypeArray pCurrentParameters, AggregateType pCurrentType, ExprMemberGroup pGroup, ArgInfos pArguments) { @@ -480,6 +486,7 @@ internal static bool ReOrderArgsForNamedArguments( ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static Expr GenerateOptionalArgument(MethodOrPropertySymbol methprop, CType type, int index) { CType pParamType = type; @@ -657,6 +664,7 @@ private bool HasOptionalParameters() // Returns true if we can either add enough optional parameters to make the // argument list match, or if we don't need to at all. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool AddArgumentsForOptionalParameters() { if (_pCurrentParameters.Count <= _pArguments.carg) @@ -858,6 +866,7 @@ private bool ConstructExpandedParameters() return TryGetExpandedParams(_pCurrentSym.Params, _pArguments.carg, out _pCurrentParameters); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Result DetermineCurrentTypeArgs() { TypeArray typeArgs = _pGroup.TypeArgs; @@ -908,6 +917,7 @@ private Result DetermineCurrentTypeArgs() return Result.Success; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool ArgumentsAreConvertible() { bool containsErrorSym = false; @@ -992,6 +1002,7 @@ private bool ArgumentsAreConvertible() return true; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private void UpdateArguments() { // Parameter types might have changed as a result of @@ -1070,6 +1081,7 @@ private static bool DoesTypeArgumentsContainErrorSym(CType var) // ---------------------------------------------------------------------------- + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private void ReportErrorsOnSuccess() { // used for Methods and Indexers @@ -1089,6 +1101,7 @@ private void ReportErrorsOnSuccess() } } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private RuntimeBinderException ReportErrorsOnFailure() { // First and foremost, report if the user specified a name more than once. diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ImplicitConversion.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ImplicitConversion.cs index d6746409ebfbd0..3b0523d62ddbe9 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ImplicitConversion.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ImplicitConversion.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Microsoft.CSharp.RuntimeBinder.Syntax; namespace Microsoft.CSharp.RuntimeBinder.Semantics @@ -53,6 +54,7 @@ public ImplicitConversion(ExpressionBinder binder, Expr exprSrc, CType typeSrc, * * returns true if the conversion can be made, false if not. */ + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public bool Bind() { // 13.1 Implicit conversions @@ -258,6 +260,7 @@ of times. (Handled by BindExplicitConversion.) Some nullable conversion are NOT standard conversions. In particular, if S => T is implicit then S? => T is not standard. Similarly if S => T is not implicit then S => T? is not standard. ***************************************************************************************************/ + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool BindNubConversion(NullableType nubDst) { // This code assumes that STANDARD and ISEXPLICIT are never both set. @@ -389,6 +392,7 @@ private bool BindNubConversion(NullableType nubDst) return true; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool bindImplicitConversionFromNull() { // null type can be implicitly converted to any reference type or pointer type or type @@ -413,6 +417,7 @@ private bool bindImplicitConversionFromNull() return true; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool bindImplicitConversionFromNullable(NullableType nubSrc) { // We can convert T? using a boxing conversion, we can convert it to ValueType, and @@ -457,6 +462,7 @@ private bool bindImplicitConversionFromNullable(NullableType nubSrc) return 0 == (_flags & CONVERTTYPE.NOUDC) && _binder.bindUserDefinedConversion(_exprSrc, nubSrc, _typeDest, _needsExprDest, out _exprDest, true); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool bindImplicitConversionFromArray() { // 13.1.4 @@ -503,6 +509,7 @@ private bool bindImplicitConversionFromArray() return true; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool bindImplicitConversionFromPointer() { // 27.4 Pointer conversions @@ -525,6 +532,7 @@ private bool bindImplicitConversionFromPointer() return false; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool bindImplicitConversionFromAgg(AggregateType aggTypeSrc) { // GENERICS: The case for constructed types is very similar to types with @@ -559,6 +567,7 @@ private bool bindImplicitConversionFromAgg(AggregateType aggTypeSrc) return bindImplicitConversionToBase(aggTypeSrc); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool bindImplicitConversionToBase(AggregateType pSource) { // 13.1.4 Implicit reference conversions @@ -588,6 +597,7 @@ private bool bindImplicitConversionToBase(AggregateType pSource) return true; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool bindImplicitConversionFromEnum(AggregateType aggTypeSrc) { // 13.1.5 Boxing conversions @@ -654,6 +664,7 @@ private bool bindImplicitConversionToEnum(AggregateType aggTypeSrc) return false; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool bindImplicitConversionBetweenSimpleTypes(AggregateType aggTypeSrc) { AggregateSymbol aggSrc = aggTypeSrc.OwningAggregate; diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MemberLookup.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MemberLookup.cs index ba90b4d88a42e0..b27c38fc9ba9c9 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MemberLookup.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MemberLookup.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Microsoft.CSharp.RuntimeBinder.Errors; using Microsoft.CSharp.RuntimeBinder.Syntax; @@ -94,6 +95,7 @@ Search just the given type (not any bases). Returns true iff it finds pfHideByName is set to true iff something was found that hides all members of base types (eg, a hidebyname method). ******************************************************************************/ + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool SearchSingleType(AggregateType typeCur, out bool pfHideByName) { bool fFoundSome = false; @@ -352,6 +354,7 @@ to null. Returns true when searching should continue to the interfaces. ******************************************************************************/ + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool LookupInClass(AggregateType typeStart, ref AggregateType ptypeEnd) { Debug.Assert(!_swtFirst || _fMulti); @@ -396,6 +399,7 @@ private bool LookupInClass(AggregateType typeStart, ref AggregateType ptypeEnd) /****************************************************************************** Returns true if searching should continue to object. ******************************************************************************/ + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool LookupInInterfaces(AggregateType typeStart, TypeArray types) { Debug.Assert(!_swtFirst || _fMulti); @@ -516,6 +520,7 @@ public MemberLookup() flags - See MemLookFlags. TypeVarsAllowed only applies to the most derived type (not base types). ***************************************************************************************************/ + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public bool Lookup(CType typeSrc, Expr obj, ParentSymbol symWhere, Name name, int arity, MemLookFlags flags) { Debug.Assert((flags & ~MemLookFlags.All) == 0); @@ -588,6 +593,7 @@ public SymWithType SwtFirst() /****************************************************************************** Reports errors. Only call this if FError() is true. ******************************************************************************/ + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public Exception ReportErrors() { Debug.Assert(FError()); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MethodIterator.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MethodIterator.cs index 764189e8174083..17243de306540a 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MethodIterator.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MethodIterator.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Microsoft.CSharp.RuntimeBinder.Syntax; namespace Microsoft.CSharp.RuntimeBinder.Semantics @@ -55,6 +56,7 @@ public CMethodIterator(Name name, TypeArray containingTypes, CType qualifyingTyp public bool CanUseCurrentSymbol { + [RequiresUnreferencedCode(Binder.TrimmerWarning)] get { // Make sure that whether we're seeing a ctor is consistent with the flag. diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MethodTypeInferrer.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MethodTypeInferrer.cs index ce8902226a542b..39cbc4479a66a7 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MethodTypeInferrer.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MethodTypeInferrer.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Microsoft.CSharp.RuntimeBinder.Syntax; namespace Microsoft.CSharp.RuntimeBinder.Semantics @@ -78,6 +79,7 @@ Initially each CType variable Xi is unfixed with an empty set of bounds. //////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static bool Infer( ExpressionBinder binder, MethodSymbol pMethod, @@ -224,6 +226,7 @@ private bool HasBound(int iParam) // Phases // + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool InferTypeArgs() { // SPEC: CType inference takes place in phases. Each phase will try to infer CType @@ -245,6 +248,7 @@ private static bool IsReallyAType(CType pType) => // The first phase // + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private void InferTypeArgsFirstPhase() { Debug.Assert(_pMethodFormalParameterTypes != null); @@ -353,6 +357,7 @@ private void InferTypeArgsFirstPhase() // The second phase // + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool InferTypeArgsSecondPhase() { // SPEC: The second phase proceeds as follows: @@ -406,6 +411,7 @@ private bool InferTypeArgsSecondPhase() //////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private NewInferenceResult DoSecondPhase() { // SPEC: If no unfixed CType parameters exist then CType inference succeeds. @@ -455,6 +461,7 @@ private NewInferenceResult DoSecondPhase() //////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private NewInferenceResult FixNondependentParameters() { // SPEC: Otherwise, if there exists one or more CType parameters Xi such that @@ -496,6 +503,7 @@ private NewInferenceResult FixNondependentParameters() //////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private NewInferenceResult FixDependentParameters() { // SPEC: All unfixed CType parameters Xi are fixed for which all of the following hold: @@ -881,6 +889,7 @@ private void ExactTypeArgumentInference( // // Lower-bound inferences // + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private void LowerBoundInference(CType pSource, CType pDest) { // SPEC: A lower-bound inference from a CType U to a CType V is made as follows: @@ -968,6 +977,7 @@ private bool LowerBoundTypeParameterInference(CType pSource, CType pDest) //////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool LowerBoundArrayInference(CType pSource, CType pDest) { // SPEC: Otherwise, if U is an array CType Ue[...] and V is either an array @@ -1056,6 +1066,7 @@ bool LowerBoundNullableInference(CType pSource, CType pDest) //////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool LowerBoundConstructedInference(CType pSource, CType pDest) { if (!(pDest is AggregateType pConstructedDest)) @@ -1118,6 +1129,7 @@ private bool LowerBoundConstructedInference(CType pSource, CType pDest) //////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool LowerBoundClassInference(CType pSource, AggregateType pDest) { if (!pDest.IsClassType) @@ -1159,6 +1171,7 @@ private bool LowerBoundClassInference(CType pSource, AggregateType pDest) //////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool LowerBoundInterfaceInference(CType pSource, AggregateType pDest) { if (!pDest.IsInterfaceType) @@ -1204,6 +1217,7 @@ private bool LowerBoundInterfaceInference(CType pSource, AggregateType pDest) return false; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private void LowerBoundTypeArgumentInference( AggregateType pSource, AggregateType pDest) { @@ -1260,6 +1274,7 @@ private void LowerBoundTypeArgumentInference( // // Upper-bound inferences // + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private void UpperBoundInference(CType pSource, CType pDest) { // SPEC: An upper-bound inference from a CType U to a CType V is made as follows: @@ -1321,6 +1336,7 @@ private bool UpperBoundTypeParameterInference(CType pSource, CType pDest) //////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool UpperBoundArrayInference(CType pSource, CType pDest) { // SPEC: Otherwise, if V is an array CType Ve[...] and U is an array @@ -1380,6 +1396,7 @@ private bool UpperBoundArrayInference(CType pSource, CType pDest) //////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool UpperBoundConstructedInference(CType pSource, CType pDest) { if (!(pSource is AggregateType pConstructedSource)) @@ -1435,6 +1452,7 @@ private bool UpperBoundConstructedInference(CType pSource, CType pDest) //////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool UpperBoundClassInference(AggregateType pSource, CType pDest) { if (!pSource.IsClassType || !pDest.IsClassType) @@ -1464,6 +1482,7 @@ private bool UpperBoundClassInference(AggregateType pSource, CType pDest) //////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool UpperBoundInterfaceInference(AggregateType pSource, CType pDest) { if (!pSource.IsInterfaceType) @@ -1505,6 +1524,7 @@ private bool UpperBoundInterfaceInference(AggregateType pSource, CType pDest) return false; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private void UpperBoundTypeArgumentInference( AggregateType pSource, AggregateType pDest) { @@ -1561,6 +1581,8 @@ private void UpperBoundTypeArgumentInference( // // Fixing // + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool Fix(int iParam) { Debug.Assert(IsUnfixed(iParam)); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Nullable.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Nullable.cs index 39c1ddfcec4726..ccdd55c5d632e0 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Nullable.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Nullable.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; namespace Microsoft.CSharp.RuntimeBinder.Semantics { @@ -34,6 +35,7 @@ private static Expr StripNullableConstructor(Expr pExpr) } // Create an expr for exprSrc.Value where exprSrc.type is a NullableType. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static Expr BindNubValue(Expr exprSrc) { Debug.Assert(exprSrc != null && exprSrc.Type is NullableType); @@ -57,6 +59,7 @@ private static Expr BindNubValue(Expr exprSrc) } // Create an expr for new T?(exprSrc) where T is exprSrc.type. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static ExprCall BindNubNew(Expr exprSrc) { Debug.Assert(exprSrc != null); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Operators.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Operators.cs index 6aad1a081daefd..d11df5adba186c 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Operators.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Operators.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Microsoft.CSharp.RuntimeBinder.Errors; using Microsoft.CSharp.RuntimeBinder.Syntax; @@ -133,7 +134,7 @@ Note that pointer operators cannot be lifted over nullable and are not callable new UnaOpSig( PredefinedType.PT_DECIMAL, UnaOpMask.IncDec, 0, null, UnaOpFuncKind.None ), }; - + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private ExprBinOp BindUserDefinedBinOp(ExpressionKind ek, BinOpArgInfo info) { MethPropWithInst pmpwi; @@ -170,6 +171,7 @@ private ExprBinOp BindUserDefinedBinOp(ExpressionKind ek, BinOpArgInfo info) // Adds special signatures to the candidate list. If we find an exact match // then it will be the last item on the list and we return true. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool GetSpecialBinopSignatures(List prgbofs, BinOpArgInfo info) { Debug.Assert(prgbofs != null); @@ -185,6 +187,7 @@ private bool GetSpecialBinopSignatures(List prgbofs, BinOpArgInfo // Adds standard and lifted signatures to the candidate list. If we find an exact match // then it will be the last item on the list and we return true. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool GetStandardAndLiftedBinopSignatures(List rgbofs, BinOpArgInfo info) { Debug.Assert(rgbofs != null); @@ -388,6 +391,7 @@ private bool GetStandardAndLiftedBinopSignatures(List rgbofs, BinO } // Returns the index of the best match, or -1 if there is no best match. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private int FindBestSignatureInList( List binopSignatures, BinOpArgInfo info) @@ -444,6 +448,7 @@ private int FindBestSignatureInList( return bestSignature; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static ExprBinOp BindNullEqualityComparison(ExpressionKind ek, BinOpArgInfo info) { Expr arg1 = info.arg1; @@ -476,6 +481,8 @@ private static ExprBinOp BindNullEqualityComparison(ExpressionKind ek, BinOpArgI This handles binding binary operators by first checking for user defined operators, then applying overload resolution to the predefined operators. It handles lifting over nullable. */ + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public Expr BindStandardBinop(ExpressionKind ek, Expr arg1, Expr arg2) { Debug.Assert(arg1 != null); @@ -550,6 +557,7 @@ public Expr BindStandardBinop(ExpressionKind ek, Expr arg1, Expr arg2) return BindStandardBinopCore(info, binopSignatures[bestBinopSignature], ek, flags); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expr BindStandardBinopCore(BinOpArgInfo info, BinOpFullSig bofs, ExpressionKind ek, EXPRFLAG flags) { if (bofs.pfn == null) @@ -591,6 +599,7 @@ private Expr BindStandardBinopCore(BinOpArgInfo info, BinOpFullSig bofs, Express return BindLiftedStandardBinOp(info, bofs, ek, flags); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private ExprBinOp BindLiftedStandardBinOp(BinOpArgInfo info, BinOpFullSig bofs, ExpressionKind ek, EXPRFLAG flags) { Debug.Assert(bofs.Type1() is NullableType || bofs.Type2() is NullableType); @@ -644,6 +653,7 @@ private ExprBinOp BindLiftedStandardBinOp(BinOpArgInfo info, BinOpFullSig bofs, ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private void LiftArgument(Expr pArgument, CType pParameterType, bool bConvertBeforeLift, out Expr ppLiftedArgument, out Expr ppNonLiftedArgument) { @@ -678,6 +688,7 @@ private void LiftArgument(Expr pArgument, CType pParameterType, bool bConvertBef Get the special signatures when at least one of the args is a delegate instance. Returns true iff an exact signature match is found. */ + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool GetDelBinOpSigs(List prgbofs, BinOpArgInfo info) { if (!info.ValidForDelegate() || !info.type1.IsDelegateType && !info.type2.IsDelegateType) @@ -717,6 +728,7 @@ private bool GetDelBinOpSigs(List prgbofs, BinOpArgInfo info) Utility method to determine whether arg1 is convertible to typeDst, either in a regular scenario or lifted scenario. Sets pgrflt, ptypeSig1 and ptypeSig2 accordingly. */ + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool CanConvertArg1(BinOpArgInfo info, CType typeDst, out LiftFlags pgrflt, out CType ptypeSig1, out CType ptypeSig2) { @@ -751,6 +763,7 @@ private bool CanConvertArg1(BinOpArgInfo info, CType typeDst, out LiftFlags pgrf /* Same as CanConvertArg1 but with the indices interchanged! */ + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool CanConvertArg2(BinOpArgInfo info, CType typeDst, out LiftFlags pgrflt, out CType ptypeSig1, out CType ptypeSig2) { @@ -786,6 +799,7 @@ private bool CanConvertArg2(BinOpArgInfo info, CType typeDst, out LiftFlags pgrf Record the appropriate binary operator full signature from the given BinOpArgInfo. This assumes that any NullableType valued args should be lifted. */ + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static void RecordBinOpSigFromArgs(List prgbofs, BinOpArgInfo info) { LiftFlags grflt = LiftFlags.None; @@ -821,6 +835,7 @@ private static void RecordBinOpSigFromArgs(List prgbofs, BinOpArgI Get the special signatures when at least one of the args is an enum. Return true if we find an exact match. */ + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool GetEnumBinOpSigs(List prgbofs, BinOpArgInfo info) { if (!info.typeRaw1.IsEnumType && !info.typeRaw2.IsEnumType) @@ -892,6 +907,7 @@ private static bool IsEnumArithmeticBinOp(ExpressionKind ek, BinOpArgInfo info) may be applicable and better (or ambiguous)! This also handles == on System.Delegate, since it has special rules as well. */ + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool GetRefEqualSigs(List prgbofs, BinOpArgInfo info) { if (info.mask != BinOpMask.Equal) @@ -1012,6 +1028,7 @@ Because of user defined conversion operators this relation is not transitive. Returns negative if ibos1 is better, positive if ibos2 is better, 0 if neither. */ + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private int WhichBofsIsBetter(BinOpFullSig bofs1, BinOpFullSig bofs2, CType type1, CType type2) { BetterType bt1; @@ -1133,6 +1150,7 @@ private static (ExpressionKind, UnaOpKind, EXPRFLAG) CalculateExprAndUnaryOpKind return (ek, uok, flags); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public Expr BindStandardUnaryOperator(OperatorKind op, Expr pArgument) { Debug.Assert(pArgument != null); @@ -1276,6 +1294,7 @@ public Expr BindStandardUnaryOperator(OperatorKind op, Expr pArgument) ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private UnaryOperatorSignatureFindResult PopulateSignatureList(Expr pArgument, UnaOpKind unaryOpKind, UnaOpMask unaryOpMask, ExpressionKind exprKind, EXPRFLAG flags, List pSignatures, out Expr ppResult) { // We should have already checked argument != null and argument.type != null. @@ -1366,6 +1385,7 @@ private UnaryOperatorSignatureFindResult PopulateSignatureList(Expr pArgument, U ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool FindApplicableSignatures( Expr pArgument, UnaOpMask unaryOpMask, @@ -1491,6 +1511,7 @@ private bool FindApplicableSignatures( return false; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private ExprOperator BindLiftedStandardUnop(ExpressionKind ek, EXPRFLAG flags, Expr arg, UnaOpFullSig uofs) { NullableType type = uofs.GetType() as NullableType; @@ -1516,6 +1537,7 @@ private ExprOperator BindLiftedStandardUnop(ExpressionKind ek, EXPRFLAG flags, E Determine which UnaOpSig is better for overload resolution. Returns negative if iuos1 is better, positive if iuos2 is better, 0 if neither. */ + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private int WhichUofsIsBetter(UnaOpFullSig uofs1, UnaOpFullSig uofs2, CType typeArg) { BetterType bt; @@ -1582,6 +1604,7 @@ private static ExprOperator BindRealUnaOp(ExpressionBinder binder, ExpressionKin /* Handles standard increment and decrement operators. */ + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expr BindIncOp(ExpressionKind ek, EXPRFLAG flags, Expr arg, UnaOpFullSig uofs) { Debug.Assert(ek == ExpressionKind.Add || ek == ExpressionKind.Subtract); @@ -1605,6 +1628,7 @@ private Expr BindIncOp(ExpressionKind ek, EXPRFLAG flags, Expr arg, UnaOpFullSig } } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expr BindIncOpCore(ExpressionKind ek, EXPRFLAG flags, Expr exprVal, CType type) { Debug.Assert(ek == ExpressionKind.Add || ek == ExpressionKind.Subtract); @@ -1662,6 +1686,7 @@ private Expr BindIncOpCore(ExpressionKind ek, EXPRFLAG flags, Expr exprVal, CTyp return LScalar(ek, flags, exprVal, type, cv, type); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expr LScalar(ExpressionKind ek, EXPRFLAG flags, Expr exprVal, CType type, ConstVal cv, CType typeTmp) { CType typeOne = type; @@ -1675,6 +1700,7 @@ private Expr LScalar(ExpressionKind ek, EXPRFLAG flags, Expr exprVal, CType type return typeTmp != type ? mustCast(pExprResult, type, CONVERTTYPE.NOUDC) : pExprResult; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private ExprMulti BindNonliftedIncOp(ExpressionKind ek, EXPRFLAG flags, Expr arg, UnaOpFullSig uofs) { Debug.Assert(ek == ExpressionKind.Add || ek == ExpressionKind.Subtract); @@ -1701,6 +1727,7 @@ private ExprMulti BindNonliftedIncOp(ExpressionKind ek, EXPRFLAG flags, Expr arg return exprMulti; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private ExprMulti BindLiftedIncOp(ExpressionKind ek, EXPRFLAG flags, Expr arg, UnaOpFullSig uofs) { Debug.Assert(ek == ExpressionKind.Add || ek == ExpressionKind.Subtract); @@ -1733,6 +1760,8 @@ Handles standard binary decimal based operators. This function is called twice by the EE for every binary operator it evaluates Here is how it works. */ + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "All types used here are builtin and will not be trimmed.")] private static ExprBinOp BindDecBinOp(ExpressionBinder _, ExpressionKind ek, EXPRFLAG flags, Expr arg1, Expr arg2) { Debug.Assert(arg1.Type.IsPredefType(PredefinedType.PT_DECIMAL) && arg2.Type.IsPredefType(PredefinedType.PT_DECIMAL)); @@ -1772,6 +1801,9 @@ private static ExprBinOp BindDecBinOp(ExpressionBinder _, ExpressionKind ek, EXP /* Handles standard unary decimal based operators. */ + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "All types used here are builtin and will not be trimmed.")] private static ExprUnaryOp BindDecUnaOp(ExpressionBinder _, ExpressionKind ek, EXPRFLAG flags, Expr arg) { Debug.Assert(arg.Type.IsPredefType(PredefinedType.PT_DECIMAL)); @@ -1804,6 +1836,8 @@ private static Expr BindStrBinOp(ExpressionBinder _, ExpressionKind ek, EXPRFLAG Bind a shift operator: <<, >>. These can have integer or long first operands, and second operand must be int. */ + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "All types used here are builtin and will not be trimmed.")] private static ExprBinOp BindShiftOp(ExpressionBinder _, ExpressionKind ek, EXPRFLAG flags, Expr arg1, Expr arg2) { Debug.Assert(ek == ExpressionKind.LeftShirt || ek == ExpressionKind.RightShift); @@ -1820,6 +1854,8 @@ private static ExprBinOp BindShiftOp(ExpressionBinder _, ExpressionKind ek, EXPR Bind a bool binary operator: ==, !=, &&, ||, , |, ^. If both operands are constant, the result will be a constant also. */ + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "All types used here are builtin and will not be trimmed.")] private static ExprBinOp BindBoolBinOp(ExpressionBinder _, ExpressionKind ek, EXPRFLAG flags, Expr arg1, Expr arg2) { Debug.Assert(arg1 != null); @@ -1830,6 +1866,7 @@ private static ExprBinOp BindBoolBinOp(ExpressionBinder _, ExpressionKind ek, EX return ExprFactory.CreateBinop(ek, GetPredefindType(PredefinedType.PT_BOOL), arg1, arg2); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private ExprOperator BindBoolBitwiseOp(ExpressionKind ek, EXPRFLAG flags, Expr expr1, Expr expr2) { Debug.Assert(ek == ExpressionKind.BitwiseAnd || ek == ExpressionKind.BitwiseOr); @@ -1867,11 +1904,15 @@ private ExprOperator BindBoolBitwiseOp(ExpressionKind ek, EXPRFLAG flags, Expr e return BindBoolBinOp(this, ek, flags, expr1, expr2); } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "All types used here are builtin and will not be trimmed.")] private static Expr BindLiftedBoolBitwiseOp(ExpressionBinder _, ExpressionKind ek, EXPRFLAG flags, Expr expr1, Expr expr2) => null; /* Handles boolean unary operator (!). */ + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "All types used here are builtin and will not be trimmed.")] private static Expr BindBoolUnaOp(ExpressionBinder _, ExpressionKind ek, EXPRFLAG flags, Expr arg) { Debug.Assert(arg.Type.IsPredefType(PredefinedType.PT_BOOL)); @@ -1895,6 +1936,8 @@ private static Expr BindBoolUnaOp(ExpressionBinder _, ExpressionKind ek, EXPRFLA /* Handles string equality. */ + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "All types used here are builtin and will not be trimmed.")] private static ExprBinOp BindStrCmpOp(ExpressionBinder _, ExpressionKind ek, EXPRFLAG flags, Expr arg1, Expr arg2) { Debug.Assert(ek == ExpressionKind.Eq || ek == ExpressionKind.NotEq); @@ -1912,6 +1955,7 @@ private static ExprBinOp BindStrCmpOp(ExpressionBinder _, ExpressionKind ek, EXP /* Handles reference equality operators. Type variables come through here. */ + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static ExprBinOp BindRefCmpOp(ExpressionBinder binder, ExpressionKind ek, EXPRFLAG flags, Expr arg1, Expr arg2) { Debug.Assert(ek == ExpressionKind.Eq || ek == ExpressionKind.NotEq); @@ -1927,6 +1971,7 @@ private static ExprBinOp BindRefCmpOp(ExpressionBinder binder, ExpressionKind ek /* Handles delegate binary operators. */ + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static Expr BindDelBinOp(ExpressionBinder _, ExpressionKind ek, EXPRFLAG flags, Expr arg1, Expr arg2) { Debug.Assert(ek == ExpressionKind.Add || ek == ExpressionKind.Subtract || ek == ExpressionKind.Eq || ek == ExpressionKind.NotEq); @@ -1967,6 +2012,7 @@ private static Expr BindDelBinOp(ExpressionBinder _, ExpressionKind ek, EXPRFLAG /* Handles enum binary operators. */ + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static Expr BindEnumBinOp(ExpressionBinder binder, ExpressionKind ek, EXPRFLAG flags, Expr arg1, Expr arg2) { AggregateType typeDst = GetEnumBinOpType(ek, arg1.Type, arg2.Type, out AggregateType typeEnum); @@ -1995,6 +2041,7 @@ private static Expr BindEnumBinOp(ExpressionBinder binder, ExpressionKind ek, EX return exprRes; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expr BindLiftedEnumArithmeticBinOp(ExpressionKind ek, EXPRFLAG flags, Expr arg1, Expr arg2) { Debug.Assert(ek == ExpressionKind.Add || ek == ExpressionKind.Subtract); @@ -2041,6 +2088,7 @@ private Expr BindLiftedEnumArithmeticBinOp(ExpressionKind ek, EXPRFLAG flags, Ex /* Handles enum unary operator (~). */ + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static Expr BindEnumUnaOp(ExpressionBinder binder, ExpressionKind ek, EXPRFLAG flags, Expr arg) { Debug.Assert(ek == ExpressionKind.BitwiseNot); @@ -2140,6 +2188,8 @@ private static Expr BindEnumUnaOp(ExpressionBinder binder, ExpressionKind ek, EX Convert an expression involving I4, U4, I8 or U8 operands. The operands are assumed to be already converted to the correct types. */ + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "All types used here are builtin and will not be trimmed.")] private ExprOperator BindIntOp(ExpressionKind kind, EXPRFLAG flags, Expr op1, Expr op2, PredefinedType ptOp) { //Debug.Assert(kind.isRelational() || kind.isArithmetic() || kind.isBitwise()); @@ -2163,6 +2213,8 @@ private ExprOperator BindIntOp(ExpressionKind kind, EXPRFLAG flags, Expr op1, Ex return exprRes; } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "All types used here are builtin and will not be trimmed.")] private ExprOperator BindIntegerNeg(EXPRFLAG flags, Expr op, PredefinedType ptOp) { // 14.6.2 Unary minus operator @@ -2220,6 +2272,9 @@ private ExprOperator BindIntegerNeg(EXPRFLAG flags, Expr op, PredefinedType ptOp will be a constant also. op2 can be null for a unary operator. The operands are assumed to be already converted to the correct type. */ + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "All types used here are builtin and will not be trimmed.")] private static ExprOperator BindFloatOp(ExpressionKind kind, Expr op1, Expr op2) { //Debug.Assert(kind.isRelational() || kind.isArithmetic()); @@ -2235,6 +2290,8 @@ private static ExprOperator BindFloatOp(ExpressionKind kind, Expr op1, Expr op2) return exprRes; } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "All types used here are builtin and will not be trimmed.")] private static ExprConcat BindStringConcat(Expr op1, Expr op2) { // If the concatenation consists solely of two constants then we must @@ -2275,6 +2332,7 @@ private static RuntimeBinderException AmbiguousOperatorError(Expr op1, Expr op2) : ErrorHandling.Error(ErrorCode.ERR_AmbigUnaryOp, strOp, op1.Type); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expr BindUserBoolOp(ExpressionKind kind, ExprCall pCall) { Debug.Assert(pCall != null); @@ -2394,6 +2452,7 @@ private static bool UserDefinedBinaryOperatorCanBeLifted(ExpressionKind ek, Meth // If the operator is applicable in either its regular or lifted forms, // add it to the candidate set and return true, otherwise return false. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool UserDefinedBinaryOperatorIsApplicable(List candidateList, ExpressionKind ek, MethodSymbol method, AggregateType ats, Expr arg1, Expr arg2, bool fDontLift) { @@ -2431,6 +2490,7 @@ private bool UserDefinedBinaryOperatorIsApplicable(List return true; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private bool GetApplicableUserDefinedBinaryOperatorCandidates( List candidateList, ExpressionKind ek, AggregateType type, Expr arg1, Expr arg2, bool fDontLift) @@ -2450,6 +2510,7 @@ private bool GetApplicableUserDefinedBinaryOperatorCandidates( return foundSome; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private AggregateType GetApplicableUserDefinedBinaryOperatorCandidatesInBaseTypes( List candidateList, ExpressionKind ek, AggregateType type, Expr arg1, Expr arg2, bool fDontLift, AggregateType atsStop) @@ -2465,6 +2526,7 @@ private AggregateType GetApplicableUserDefinedBinaryOperatorCandidatesInBaseType return null; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private ExprCall BindUDBinop(ExpressionKind ek, Expr arg1, Expr arg2, bool fDontLift, out MethPropWithInst ppmpwi) { List methFirst = new List(); @@ -2523,6 +2585,7 @@ private ExprCall BindUDBinop(ExpressionKind ek, Expr arg1, Expr arg2, bool fDont return BindUDBinopCall(arg1, arg2, pmethBest.@params, typeRetRaw, pmethBest.mpwi); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private ExprCall BindUDBinopCall(Expr arg1, Expr arg2, TypeArray Params, CType typeRet, MethPropWithInst mpwi) { arg1 = mustConvert(arg1, Params[0]); @@ -2541,6 +2604,7 @@ private ExprCall BindUDBinopCall(Expr arg1, Expr arg2, TypeArray Params, CType t return call; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private ExprCall BindLiftedUDBinop(ExpressionKind ek, Expr arg1, Expr arg2, TypeArray Params, MethPropWithInst mpwi) { Expr exprVal1 = arg1; @@ -2618,6 +2682,7 @@ private ExprCall BindLiftedUDBinop(ExpressionKind ek, Expr arg1, Expr arg2, Type return call; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static AggregateType GetEnumBinOpType(ExpressionKind ek, CType argType1, CType argType2, out AggregateType ppEnumType) { Debug.Assert(argType1.IsEnumType || argType2.IsEnumType); @@ -2659,6 +2724,7 @@ private static AggregateType GetEnumBinOpType(ExpressionKind ek, CType argType1, return typeDst; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static ExprBinOp CreateBinopForPredefMethodCall(ExpressionKind ek, PREDEFMETH predefMeth, CType RetType, Expr arg1, Expr arg2) { MethodSymbol methSym = PredefinedMembers.GetMethod(predefMeth); @@ -2673,6 +2739,7 @@ private static ExprBinOp CreateBinopForPredefMethodCall(ExpressionKind ek, PREDE return binop; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static ExprUnaryOp CreateUnaryOpForPredefMethodCall(ExpressionKind ek, PREDEFMETH predefMeth, CType pRetType, Expr pArg) { MethodSymbol methSym = PredefinedMembers.GetMethod(predefMeth); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/PredefinedMembers.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/PredefinedMembers.cs index ee501afe1ac057..a6f8b265502441 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/PredefinedMembers.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/PredefinedMembers.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Microsoft.CSharp.RuntimeBinder.Syntax; namespace Microsoft.CSharp.RuntimeBinder.Semantics @@ -197,12 +198,14 @@ internal static class PredefinedMembers private static readonly MethodSymbol[] _methods = new MethodSymbol[(int)PREDEFMETH.PM_COUNT]; private static readonly PropertySymbol[] _properties = new PropertySymbol[(int)PREDEFPROP.PP_COUNT]; + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static PropertySymbol LoadProperty(PREDEFPROP property) { PredefinedPropertyInfo info = GetPropInfo(property); return LoadProperty(property, NameManager.GetPredefinedName(info.name), info.getter); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static PropertySymbol LoadProperty( PREDEFPROP predefProp, Name propertyName, @@ -221,8 +224,10 @@ private static PropertySymbol LoadProperty( return property; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static AggregateSymbol GetPredefAgg(PredefinedType pt) => SymbolLoader.GetPredefAgg(pt); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static CType LoadTypeFromSignature(int[] signature, ref int indexIntoSignatures, TypeArray classTyVars) { Debug.Assert(signature != null); @@ -263,6 +268,7 @@ private static CType LoadTypeFromSignature(int[] signature, ref int indexIntoSig } } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static TypeArray LoadTypeArrayFromSignature(int[] signature, ref int indexIntoSignatures, TypeArray classTyVars) { Debug.Assert(signature != null); @@ -296,18 +302,21 @@ static PredefinedMembers() } #endif + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static PropertySymbol GetProperty(PREDEFPROP property) { Debug.Assert(property >= 0 && property < PREDEFPROP.PP_COUNT); return _properties[(int)property] ?? (_properties[(int)property] = LoadProperty(property)); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static MethodSymbol GetMethod(PREDEFMETH method) { Debug.Assert(method >= 0 && method < PREDEFMETH.PM_COUNT); return _methods[(int)method] ?? (_methods[(int)method] = LoadMethod(method)); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static MethodSymbol LoadMethod( AggregateSymbol type, int[] signature, @@ -363,6 +372,7 @@ private static MethodSymbol LookupMethodWhileLoading(AggregateSymbol type, int c return null; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static MethodSymbol LoadMethod(PREDEFMETH method) { PredefinedMethodInfo info = GetMethInfo(method); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/SemanticChecker.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/SemanticChecker.cs index e90e98f880e587..620094db1ef9f9 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/SemanticChecker.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/SemanticChecker.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Microsoft.CSharp.RuntimeBinder.Errors; namespace Microsoft.CSharp.RuntimeBinder.Semantics @@ -28,6 +29,7 @@ public static void CheckForStaticClass(CType type) } } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static ACCESSERROR CheckAccess2(Symbol symCheck, AggregateType atsCheck, Symbol symWhere, CType typeThru) { Debug.Assert(symCheck != null); @@ -77,6 +79,7 @@ typeThru is ArrayType || return CheckTypeAccess(type, symWhere) ? ACCESSERROR.ACCESSERROR_NOERROR : ACCESSERROR.ACCESSERROR_NOACCESS; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static bool CheckTypeAccess(CType type, Symbol symWhere) { Debug.Assert(type != null); @@ -110,6 +113,7 @@ public static bool CheckTypeAccess(CType type, Symbol symWhere) return true; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static ACCESSERROR CheckAccessCore(Symbol symCheck, AggregateType atsCheck, Symbol symWhere, CType typeThru) { Debug.Assert(symCheck != null); @@ -246,6 +250,7 @@ typeThru is ArrayType || public static bool CheckBogus(Symbol sym) => (sym as PropertySymbol)?.Bogus ?? false; + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static RuntimeBinderException ReportAccessError(SymWithType swtBad, Symbol symWhere, CType typeQual) { Debug.Assert(!CheckAccess(swtBad.Sym, swtBad.GetType(), symWhere, typeQual) || @@ -257,6 +262,7 @@ public static RuntimeBinderException ReportAccessError(SymWithType swtBad, Symbo : ErrorHandling.Error(ErrorCode.ERR_BadAccess, swtBad); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static bool CheckAccess(Symbol symCheck, AggregateType atsCheck, Symbol symWhere, CType typeThru) => CheckAccess2(symCheck, atsCheck, symWhere, typeThru) == ACCESSERROR.ACCESSERROR_NOERROR; } diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/AggregateSymbol.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/AggregateSymbol.cs index 7076d277fbbd35..a7bee97dda34ca 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/AggregateSymbol.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/AggregateSymbol.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using Microsoft.CSharp.RuntimeBinder.Syntax; @@ -220,6 +221,7 @@ public void SetSealed(bool @sealed) //////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public bool HasConversion() { SymbolTable.AddConversionsForType(AssociatedSystemType); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/SymbolLoader.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/SymbolLoader.cs index 3fa95f2ba33979..7899c66348f63c 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/SymbolLoader.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/SymbolLoader.cs @@ -2,18 +2,22 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Microsoft.CSharp.RuntimeBinder.Syntax; namespace Microsoft.CSharp.RuntimeBinder.Semantics { internal static class SymbolLoader { + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static AggregateSymbol GetPredefAgg(PredefinedType pt) => TypeManager.GetPredefAgg(pt); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static AggregateType GetPredefindType(PredefinedType pt) => GetPredefAgg(pt).getThisType(); public static Symbol LookupAggMember(Name name, AggregateSymbol agg, symbmask_t mask) => SymbolStore.LookupSym(name, agg, mask); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static bool IsBaseInterface(AggregateType atsDer, AggregateType pBase) { Debug.Assert(atsDer != null); @@ -37,6 +41,7 @@ private static bool IsBaseInterface(AggregateType atsDer, AggregateType pBase) return false; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static bool IsBaseClassOfClass(CType pDerived, CType pBase) { Debug.Assert(pDerived != null); @@ -47,6 +52,7 @@ public static bool IsBaseClassOfClass(CType pDerived, CType pBase) return pDerived.IsClassType && IsBaseClass(pDerived, pBase); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static bool IsBaseClass(CType pDerived, CType pBase) { Debug.Assert(pDerived != null); @@ -84,6 +90,7 @@ private static bool IsBaseClass(CType pDerived, CType pBase) return false; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static bool HasCovariantArrayConversion(ArrayType pSource, ArrayType pDest) { Debug.Assert(pSource != null); @@ -95,6 +102,7 @@ private static bool HasCovariantArrayConversion(ArrayType pSource, ArrayType pDe HasImplicitReferenceConversion(pSource.ElementType, pDest.ElementType); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static bool HasIdentityOrImplicitReferenceConversion(CType pSource, CType pDest) { Debug.Assert(pSource != null); @@ -109,6 +117,7 @@ public static bool HasIdentityOrImplicitReferenceConversion(CType pSource, CType private static bool AreTypesEqualForConversion(CType pType1, CType pType2) => pType1.Equals(pType2); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static bool HasArrayConversionToInterface(ArrayType pSource, CType pDest) { Debug.Assert(pSource != null); @@ -150,6 +159,7 @@ private static bool HasArrayConversionToInterface(ArrayType pSource, CType pDest return HasIdentityOrImplicitReferenceConversion(pSource.ElementType, atsDest.TypeArgsAll[0]); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static bool HasImplicitReferenceConversion(CType pSource, CType pDest) { Debug.Assert(pSource != null); @@ -276,6 +286,7 @@ private static bool HasImplicitReferenceConversion(CType pSource, CType pDest) return false; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static bool HasAnyBaseInterfaceConversion(CType pDerived, CType pBase) { if (!pBase.IsInterfaceType || !(pDerived is AggregateType atsDer)) @@ -313,6 +324,7 @@ private static bool HasAnyBaseInterfaceConversion(CType pDerived, CType pBase) // * if the ith parameter of U is contravariant then either Si is exactly // equal to Ti, or there is an implicit reference conversion from Ti to Si. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static bool HasInterfaceConversion(AggregateType pSource, AggregateType pDest) { Debug.Assert(pSource != null && pSource.IsInterfaceType); @@ -322,6 +334,7 @@ private static bool HasInterfaceConversion(AggregateType pSource, AggregateType ////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static bool HasDelegateConversion(AggregateType pSource, AggregateType pDest) { Debug.Assert(pSource != null && pSource.IsDelegateType); @@ -331,6 +344,7 @@ private static bool HasDelegateConversion(AggregateType pSource, AggregateType p ////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static bool HasVariantConversion(AggregateType pSource, AggregateType pDest) { Debug.Assert(pSource != null); @@ -385,6 +399,7 @@ private static bool HasVariantConversion(AggregateType pSource, AggregateType pD return true; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static bool HasImplicitBoxingConversion(CType pSource, CType pDest) { Debug.Assert(pSource != null); @@ -422,6 +437,7 @@ private static bool HasImplicitBoxingConversion(CType pSource, CType pDest) return IsBaseClass(pSource, pDest) || HasAnyBaseInterfaceConversion(pSource, pDest); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static bool HasBaseConversion(CType pSource, CType pDest) { // By a "base conversion" we mean: diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Cast.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Cast.cs index 5270033d5ef021..72a6b74534caa0 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Cast.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Cast.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; namespace Microsoft.CSharp.RuntimeBinder.Semantics { @@ -22,6 +23,7 @@ public ExprCast(EXPRFLAG flags, CType type, Expr argument) public override object Object { + [RequiresUnreferencedCode(Binder.TrimmerWarning)] get { Expr arg = Argument; diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Constant.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Constant.cs index 20d33816622c6f..4b27fa0280c3e0 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Constant.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Constant.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; namespace Microsoft.CSharp.RuntimeBinder.Semantics { @@ -47,6 +48,7 @@ public long Int64Value public override object Object { + [RequiresUnreferencedCode(Binder.TrimmerWarning)] get { if (Type is NullType) diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/EXPR.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/EXPR.cs index 4f4de917eb46a9..f60ef0a6e68966 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/EXPR.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/EXPR.cs @@ -41,6 +41,7 @@ public CType Type [ExcludeFromCodeCoverage(Justification = "Should only be called through override")] public virtual object Object { + [RequiresUnreferencedCode(Binder.TrimmerWarning)] get { Debug.Fail("Invalid Expr in GetObject"); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/MethodInfo.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/MethodInfo.cs index 600a4dde3eb6d1..ae36dd5e67d9fe 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/MethodInfo.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/MethodInfo.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Reflection; namespace Microsoft.CSharp.RuntimeBinder.Semantics @@ -21,6 +22,7 @@ public ExprMethodInfo(CType type, MethodSymbol method, AggregateType methodType, public MethodInfo MethodInfo { + [RequiresUnreferencedCode(Binder.TrimmerWarning)] get { // To do this, we need to construct a type array of the parameter types, @@ -93,6 +95,7 @@ public MethodInfo MethodInfo public ConstructorInfo ConstructorInfo { + [RequiresUnreferencedCode(Binder.TrimmerWarning)] get { // To do this, we need to construct a type array of the parameter types, @@ -142,6 +145,10 @@ public ConstructorInfo ConstructorInfo } } - public override object Object => MethodInfo; + public override object Object + { + [RequiresUnreferencedCode(Binder.TrimmerWarning)] + get => MethodInfo; + } } } diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/PropertyInfo.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/PropertyInfo.cs index 8d0b33c178eb5e..5e1170bd080d98 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/PropertyInfo.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/PropertyInfo.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Reflection; namespace Microsoft.CSharp.RuntimeBinder.Semantics @@ -21,6 +22,7 @@ public ExprPropertyInfo(CType type, PropertySymbol propertySymbol, AggregateType public PropertyInfo PropertyInfo { + [RequiresUnreferencedCode(Binder.TrimmerWarning)] get { // To do this, we need to construct a type array of the parameter types, diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/TypeOf.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/TypeOf.cs index 537c510750da1d..2225a397ce87ff 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/TypeOf.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/TypeOf.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; + namespace Microsoft.CSharp.RuntimeBinder.Semantics { internal sealed class ExprTypeOf : ExprWithType @@ -14,6 +16,10 @@ public ExprTypeOf(CType type, CType sourceType) public CType SourceType { get; } - public override object Object => SourceType.AssociatedSystemType; + public override object Object + { + [RequiresUnreferencedCode(Binder.TrimmerWarning)] + get => SourceType.AssociatedSystemType; + } } } diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Visitors/ExprVisitorBase.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Visitors/ExprVisitorBase.cs index c722ffa2be79f2..0799ada9e0f904 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Visitors/ExprVisitorBase.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Visitors/ExprVisitorBase.cs @@ -2,13 +2,16 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; namespace Microsoft.CSharp.RuntimeBinder.Semantics { internal abstract class ExprVisitorBase { + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected Expr Visit(Expr pExpr) => pExpr == null ? null : Dispatch(pExpr); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr Dispatch(Expr pExpr) => pExpr.Kind switch { @@ -85,6 +88,7 @@ protected virtual Expr Dispatch(Expr pExpr) => _ => throw Error.InternalCompilerError(), }; + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private void VisitChildren(Expr pExpr) { Debug.Assert(pExpr != null); @@ -289,271 +293,398 @@ a qualified name. } } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitEXPR(Expr pExpr) { VisitChildren(pExpr); return pExpr; } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitBINOP(ExprBinOp pExpr) { return VisitEXPR(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitLIST(ExprList pExpr) { return VisitEXPR(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitASSIGNMENT(ExprAssignment pExpr) { return VisitEXPR(pExpr); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitARRAYINDEX(ExprArrayIndex pExpr) { return VisitEXPR(pExpr); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitUNARYOP(ExprUnaryOp pExpr) { return VisitEXPR(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitUSERLOGOP(ExprUserLogicalOp pExpr) { return VisitEXPR(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitTYPEOF(ExprTypeOf pExpr) { return VisitEXPR(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitCAST(ExprCast pExpr) { return VisitEXPR(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitUSERDEFINEDCONVERSION(ExprUserDefinedConversion pExpr) { return VisitEXPR(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitZEROINIT(ExprZeroInit pExpr) { return VisitEXPR(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitMEMGRP(ExprMemberGroup pExpr) { return VisitEXPR(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitCALL(ExprCall pExpr) { return VisitEXPR(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitPROP(ExprProperty pExpr) { return VisitEXPR(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitFIELD(ExprField pExpr) { return VisitEXPR(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitLOCAL(ExprLocal pExpr) { return VisitEXPR(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitCONSTANT(ExprConstant pExpr) { return VisitEXPR(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitMULTIGET(ExprMultiGet pExpr) { return VisitEXPR(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitMULTI(ExprMulti pExpr) { return VisitEXPR(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitWRAP(ExprWrap pExpr) { return VisitEXPR(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitCONCAT(ExprConcat pExpr) { return VisitEXPR(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitARRINIT(ExprArrayInit pExpr) { return VisitEXPR(pExpr); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitFIELDINFO(ExprFieldInfo pExpr) { return VisitEXPR(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitMETHODINFO(ExprMethodInfo pExpr) { return VisitEXPR(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitEQUALS(ExprBinOp pExpr) { return VisitBINOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitCOMPARE(ExprBinOp pExpr) { return VisitBINOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitEQ(ExprBinOp pExpr) { return VisitBINOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitNE(ExprBinOp pExpr) { return VisitBINOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitLE(ExprBinOp pExpr) { return VisitBINOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitGE(ExprBinOp pExpr) { return VisitBINOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitADD(ExprBinOp pExpr) { return VisitBINOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitSUB(ExprBinOp pExpr) { return VisitBINOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitDIV(ExprBinOp pExpr) { return VisitBINOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitBITAND(ExprBinOp pExpr) { return VisitBINOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitBITOR(ExprBinOp pExpr) { return VisitBINOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitLSHIFT(ExprBinOp pExpr) { return VisitBINOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitLOGAND(ExprBinOp pExpr) { return VisitBINOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitSEQUENCE(ExprBinOp pExpr) { return VisitBINOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitSAVE(ExprBinOp pExpr) { return VisitBINOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitINDIR(ExprBinOp pExpr) { return VisitBINOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitSTRINGEQ(ExprBinOp pExpr) { return VisitBINOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitDELEGATEEQ(ExprBinOp pExpr) { return VisitBINOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitDELEGATEADD(ExprBinOp pExpr) { return VisitBINOP(pExpr); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitLT(ExprBinOp pExpr) { return VisitBINOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitMUL(ExprBinOp pExpr) { return VisitBINOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitBITXOR(ExprBinOp pExpr) { return VisitBINOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitRSHIFT(ExprBinOp pExpr) { return VisitBINOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitLOGOR(ExprBinOp pExpr) { return VisitBINOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitSTRINGNE(ExprBinOp pExpr) { return VisitBINOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitDELEGATENE(ExprBinOp pExpr) { return VisitBINOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitGT(ExprBinOp pExpr) { return VisitBINOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitMOD(ExprBinOp pExpr) { return VisitBINOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitSWAP(ExprBinOp pExpr) { return VisitBINOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitDELEGATESUB(ExprBinOp pExpr) { return VisitBINOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitTRUE(ExprUnaryOp pExpr) { return VisitUNARYOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitINC(ExprUnaryOp pExpr) { return VisitUNARYOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitLOGNOT(ExprUnaryOp pExpr) { return VisitUNARYOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitNEG(ExprUnaryOp pExpr) { return VisitUNARYOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitBITNOT(ExprUnaryOp pExpr) { return VisitUNARYOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitADDR(ExprUnaryOp pExpr) { return VisitUNARYOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitDECIMALNEG(ExprUnaryOp pExpr) { return VisitUNARYOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitDECIMALDEC(ExprUnaryOp pExpr) { return VisitUNARYOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitFALSE(ExprUnaryOp pExpr) { return VisitUNARYOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitDEC(ExprUnaryOp pExpr) { return VisitUNARYOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitUPLUS(ExprUnaryOp pExpr) { return VisitUNARYOP(pExpr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected virtual Expr VisitDECIMALINC(ExprUnaryOp pExpr) { return VisitUNARYOP(pExpr); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Visitors/ExpressionTreeRewriter.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Visitors/ExpressionTreeRewriter.cs index de14ce440bed50..8ca065a59748fc 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Visitors/ExpressionTreeRewriter.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Visitors/ExpressionTreeRewriter.cs @@ -2,14 +2,17 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Microsoft.CSharp.RuntimeBinder.Syntax; namespace Microsoft.CSharp.RuntimeBinder.Semantics { internal sealed class ExpressionTreeRewriter : ExprVisitorBase { + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static ExprBinOp Rewrite(ExprBoundLambda expr) => new ExpressionTreeRewriter().VisitBoundLambda(expr); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected override Expr Dispatch(Expr expr) { Debug.Assert(expr != null); @@ -24,6 +27,7 @@ protected override Expr Dispatch(Expr expr) ///////////////////////////////////////////////////////////////////////////////// // Statement types. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected override Expr VisitASSIGNMENT(ExprAssignment assignment) { Debug.Assert(assignment != null); @@ -61,10 +65,14 @@ protected override Expr VisitASSIGNMENT(ExprAssignment assignment) Expr rhs = Visit(assignment.RHS); return GenerateCall(PREDEFMETH.PM_EXPRESSION_ASSIGN, lhs, rhs); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected override Expr VisitMULTIGET(ExprMultiGet pExpr) { return Visit(pExpr.OptionalMulti.Left); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected override Expr VisitMULTI(ExprMulti pExpr) { Expr rhs = Visit(pExpr.Operator); @@ -75,6 +83,7 @@ protected override Expr VisitMULTI(ExprMulti pExpr) ///////////////////////////////////////////////////////////////////////////////// // Expression types. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private ExprBinOp VisitBoundLambda(ExprBoundLambda anonmeth) { Debug.Assert(anonmeth != null); @@ -97,17 +106,23 @@ private ExprBinOp VisitBoundLambda(ExprBoundLambda anonmeth) call.PredefinedMethod = PREDEFMETH.PM_EXPRESSION_LAMBDA; return ExprFactory.CreateSequence(createParameters, call); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected override Expr VisitCONSTANT(ExprConstant expr) { Debug.Assert(expr != null); return GenerateConstant(expr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected override Expr VisitLOCAL(ExprLocal local) { Debug.Assert(local != null); Debug.Assert(local.Local.wrap != null); return local.Local.wrap; } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected override Expr VisitFIELD(ExprField expr) { Debug.Assert(expr != null); @@ -123,11 +138,15 @@ protected override Expr VisitFIELD(ExprField expr) ExprFieldInfo pFieldInfo = ExprFactory.CreateFieldInfo(expr.FieldWithType.Field(), expr.FieldWithType.GetType()); return GenerateCall(PREDEFMETH.PM_EXPRESSION_FIELD, pObject, pFieldInfo); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected override Expr VisitUSERDEFINEDCONVERSION(ExprUserDefinedConversion expr) { Debug.Assert(expr != null); return GenerateUserDefinedConversion(expr, expr.Argument); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected override Expr VisitCAST(ExprCast pExpr) { Debug.Assert(pExpr != null); @@ -160,6 +179,8 @@ protected override Expr VisitCAST(ExprCast pExpr) } return result; } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected override Expr VisitCONCAT(ExprConcat expr) { Debug.Assert(expr != null); @@ -178,6 +199,8 @@ protected override Expr VisitCONCAT(ExprConcat expr) Expr methodInfo = ExprFactory.CreateMethodInfo(method, SymbolLoader.GetPredefindType(PredefinedType.PT_STRING), null); return GenerateCall(PREDEFMETH.PM_EXPRESSION_ADD_USER_DEFINED, p1, p2, methodInfo); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected override Expr VisitBINOP(ExprBinOp expr) { Debug.Assert(expr != null); @@ -190,6 +213,8 @@ protected override Expr VisitBINOP(ExprBinOp expr) return GenerateBuiltInBinaryOperator(expr); } } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected override Expr VisitUNARYOP(ExprUnaryOp pExpr) { Debug.Assert(pExpr != null); @@ -202,6 +227,8 @@ protected override Expr VisitUNARYOP(ExprUnaryOp pExpr) return GenerateBuiltInUnaryOperator(pExpr); } } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected override Expr VisitARRAYINDEX(ExprArrayIndex pExpr) { Debug.Assert(pExpr != null); @@ -215,6 +242,7 @@ protected override Expr VisitARRAYINDEX(ExprArrayIndex pExpr) return GenerateCall(PREDEFMETH.PM_EXPRESSION_ARRAYINDEX, arr, args); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected override Expr VisitCALL(ExprCall expr) { Debug.Assert(expr != null); @@ -278,6 +306,8 @@ protected override Expr VisitCALL(ExprCall expr) return GenerateCall(pdm, pObject, methodInfo, Params); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected override Expr VisitPROP(ExprProperty expr) { Debug.Assert(expr != null); @@ -300,6 +330,8 @@ protected override Expr VisitPROP(ExprProperty expr) } return GenerateCall(PREDEFMETH.PM_EXPRESSION_PROPERTY, pObject, propInfo); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected override Expr VisitARRINIT(ExprArrayInit expr) { Debug.Assert(expr != null); @@ -309,17 +341,22 @@ protected override Expr VisitARRINIT(ExprArrayInit expr) Expr Params = GenerateParamsArray(args, PredefinedType.PT_EXPRESSION); return GenerateCall(PREDEFMETH.PM_EXPRESSION_NEWARRAYINIT, pTypeOf, Params); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected override Expr VisitZEROINIT(ExprZeroInit expr) { Debug.Assert(expr != null); return GenerateConstant(expr); } + + [RequiresUnreferencedCode(Binder.TrimmerWarning)] protected override Expr VisitTYPEOF(ExprTypeOf expr) { Debug.Assert(expr != null); return GenerateConstant(expr); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expr GenerateDelegateInvoke(ExprCall expr) { Debug.Assert(expr != null); @@ -333,6 +370,7 @@ private Expr GenerateDelegateInvoke(ExprCall expr) return GenerateCall(PREDEFMETH.PM_EXPRESSION_INVOKE, pObject, Params); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expr GenerateBuiltInBinaryOperator(ExprBinOp expr) { Debug.Assert(expr != null); @@ -433,6 +471,7 @@ private Expr GenerateBuiltInBinaryOperator(ExprBinOp expr) return call; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expr GenerateBuiltInUnaryOperator(ExprUnaryOp expr) { Debug.Assert(expr != null); @@ -456,6 +495,7 @@ private Expr GenerateBuiltInUnaryOperator(ExprUnaryOp expr) return GenerateCall(pdm, Visit(origOp)); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expr GenerateUserDefinedBinaryOperator(ExprBinOp expr) { Debug.Assert(expr != null); @@ -535,6 +575,7 @@ private Expr GenerateUserDefinedBinaryOperator(ExprBinOp expr) return call; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expr GenerateUserDefinedUnaryOperator(ExprUnaryOp expr) { Debug.Assert(expr != null); @@ -585,6 +626,7 @@ private Expr GenerateUserDefinedUnaryOperator(ExprUnaryOp expr) return GenerateCall(pdm, op, methodInfo); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expr GenerateUserDefinedComparisonOperator(ExprBinOp expr) { Debug.Assert(expr != null); @@ -622,9 +664,11 @@ private Expr GenerateUserDefinedComparisonOperator(ExprBinOp expr) return GenerateCall(pdm, p1, p2, lift, methodInfo); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expr GenerateConversion(Expr arg, CType CType, bool bChecked) => GenerateConversionWithSource(Visit(arg), CType, bChecked || arg.isChecked()); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static Expr GenerateConversionWithSource(Expr pTarget, CType pType, bool bChecked) { PREDEFMETH pdm = bChecked ? PREDEFMETH.PM_EXPRESSION_CONVERTCHECKED : PREDEFMETH.PM_EXPRESSION_CONVERT; @@ -632,6 +676,7 @@ private static Expr GenerateConversionWithSource(Expr pTarget, CType pType, bool return GenerateCall(pdm, pTarget, pTypeOf); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expr GenerateValueAccessConversion(Expr pArgument) { Debug.Assert(pArgument != null); @@ -640,12 +685,14 @@ private Expr GenerateValueAccessConversion(Expr pArgument) return GenerateCall(PREDEFMETH.PM_EXPRESSION_CONVERT, Visit(pArgument), pStrippedTypeExpr); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expr GenerateUserDefinedConversion(Expr arg, CType type, MethWithInst method) { Expr target = Visit(arg); return GenerateUserDefinedConversion(arg, type, target, method); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static Expr GenerateUserDefinedConversion(Expr arg, CType CType, Expr target, MethWithInst method) { // The user-defined explicit conversion from enum? to decimal or decimal? requires @@ -688,6 +735,7 @@ private static Expr GenerateUserDefinedConversion(Expr arg, CType CType, Expr ta return GenerateCall(pdmOuter, callUserDefinedConversion, typeofOuter); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expr GenerateUserDefinedConversion(ExprUserDefinedConversion pExpr, Expr pArgument) { Expr pCastCall = pExpr.UserDefinedCall; @@ -736,6 +784,7 @@ private Expr GenerateUserDefinedConversion(ExprUserDefinedConversion pExpr, Expr return GenerateUserDefinedConversion(pCastArgument, pExpr.Type, pConversionSource, pExpr.UserDefinedCallMethod); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static Expr GenerateParameter(string name, CType CType) { SymbolLoader.GetPredefindType(PredefinedType.PT_STRING); // force an ensure state @@ -744,10 +793,13 @@ private static Expr GenerateParameter(string name, CType CType) return GenerateCall(PREDEFMETH.PM_EXPRESSION_PARAMETER, pTypeOf, nameString); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static MethodSymbol GetPreDefMethod(PREDEFMETH pdm) => PredefinedMembers.GetMethod(pdm); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static ExprTypeOf CreateTypeOf(CType type) => ExprFactory.CreateTypeOf(type); + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static Expr CreateWraps(ExprBoundLambda anonmeth) { Expr sequence = null; @@ -775,6 +827,7 @@ private static Expr CreateWraps(ExprBoundLambda anonmeth) return sequence; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expr GenerateConstructor(ExprCall expr) { Debug.Assert(expr != null); @@ -785,6 +838,7 @@ private Expr GenerateConstructor(ExprCall expr) return GenerateCall(PREDEFMETH.PM_EXPRESSION_NEW, constructorInfo, Params); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expr GenerateArgsList(Expr oldArgs) { Expr newArgs = null; @@ -797,6 +851,7 @@ private Expr GenerateArgsList(Expr oldArgs) return newArgs; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Expr GenerateIndexList(Expr oldIndices) { CType intType = SymbolLoader.GetPredefindType(PredefinedType.PT_INT); @@ -817,6 +872,7 @@ private Expr GenerateIndexList(Expr oldIndices) return newIndices; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static Expr GenerateConstant(Expr expr) { EXPRFLAG flags = 0; @@ -841,6 +897,7 @@ private static Expr GenerateConstant(Expr expr) return GenerateCall(PREDEFMETH.PM_EXPRESSION_CONSTANT_OBJECT_TYPE, cast, pTypeOf2); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static ExprCall GenerateCall(PREDEFMETH pdm, Expr arg1) { MethodSymbol method = GetPreDefMethod(pdm); @@ -856,6 +913,7 @@ private static ExprCall GenerateCall(PREDEFMETH pdm, Expr arg1) return call; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static ExprCall GenerateCall(PREDEFMETH pdm, Expr arg1, Expr arg2) { MethodSymbol method = GetPreDefMethod(pdm); @@ -870,6 +928,7 @@ private static ExprCall GenerateCall(PREDEFMETH pdm, Expr arg1, Expr arg2) return call; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static ExprCall GenerateCall(PREDEFMETH pdm, Expr arg1, Expr arg2, Expr arg3) { MethodSymbol method = GetPreDefMethod(pdm); @@ -884,6 +943,7 @@ private static ExprCall GenerateCall(PREDEFMETH pdm, Expr arg1, Expr arg2, Expr return call; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static ExprCall GenerateCall(PREDEFMETH pdm, Expr arg1, Expr arg2, Expr arg3, Expr arg4) { MethodSymbol method = GetPreDefMethod(pdm); @@ -898,6 +958,7 @@ private static ExprCall GenerateCall(PREDEFMETH pdm, Expr arg1, Expr arg2, Expr return call; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static ExprArrayInit GenerateParamsArray(Expr args, PredefinedType pt) { int parameterCount = ExpressionIterator.Count(args); @@ -907,6 +968,7 @@ private static ExprArrayInit GenerateParamsArray(Expr args, PredefinedType pt) return ExprFactory.CreateArrayInit(paramsArrayType, args, paramsArrayArg, new int[] { parameterCount }); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static void FixLiftedUserDefinedBinaryOperators(ExprBinOp expr, ref Expr pp1, ref Expr pp2) { // If we have lifted T1 op T2 to T1? op T2?, and we have an expression T1 op T2? or T1? op T2 then diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/ZeroInitialize.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/ZeroInitialize.cs index 1201a30fe38b9e..ec92bf378ca66d 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/ZeroInitialize.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/ZeroInitialize.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics.CodeAnalysis; namespace Microsoft.CSharp.RuntimeBinder.Semantics { @@ -12,6 +13,10 @@ public ExprZeroInit(CType type) { } - public override object Object => Activator.CreateInstance(Type.AssociatedSystemType); + public override object Object + { + [RequiresUnreferencedCode(Binder.TrimmerWarning)] + get => Activator.CreateInstance(Type.AssociatedSystemType); + } } } diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/TypeBind.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/TypeBind.cs index b46d45fb199291..664705753b75a6 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/TypeBind.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/TypeBind.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Microsoft.CSharp.RuntimeBinder.Errors; using Microsoft.CSharp.RuntimeBinder.Syntax; @@ -31,6 +32,7 @@ internal enum CheckConstraintsFlags internal static class TypeBind { // Check the constraints of any type arguments in the given Type. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static bool CheckConstraints(CType type, CheckConstraintsFlags flags) { type = type.GetNakedType(false); @@ -117,6 +119,7 @@ public static bool CheckConstraints(CType type, CheckConstraintsFlags flags) //////////////////////////////////////////////////////////////////////////////// // Check the constraints on the method instantiation. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static void CheckMethConstraints(MethWithInst mwi) { Debug.Assert(mwi.Meth() != null && mwi.GetType() != null && mwi.TypeArgs != null); @@ -133,6 +136,7 @@ public static void CheckMethConstraints(MethWithInst mwi) // Check whether typeArgs satisfies the constraints of typeVars. The // typeArgsCls and typeArgsMeth are used for substitution on the bounds. The // tree and symErr are used for error reporting. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static bool CheckConstraintsCore(Symbol symErr, TypeArray typeVars, TypeArray typeArgs, TypeArray typeArgsCls, TypeArray typeArgsMeth, CheckConstraintsFlags flags) { Debug.Assert(typeVars.Count == typeArgs.Count); @@ -154,6 +158,7 @@ private static bool CheckConstraintsCore(Symbol symErr, TypeArray typeVars, Type return true; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static bool CheckSingleConstraint(Symbol symErr, TypeParameterType var, CType arg, TypeArray typeArgsCls, TypeArray typeArgsMeth, CheckConstraintsFlags flags) { Debug.Assert(!(arg is PointerType)); @@ -298,6 +303,7 @@ private static bool CheckSingleConstraint(Symbol symErr, TypeParameterType var, // typeBnd could be just about any type (since we added naked type parameter // constraints). + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static bool SatisfiesBound(CType arg, CType typeBnd) { if (typeBnd == arg) diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/AggregateType.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/AggregateType.cs index 87d4bd745df57a..7eaf5d18a336eb 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/AggregateType.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/AggregateType.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Microsoft.CSharp.RuntimeBinder.Syntax; namespace Microsoft.CSharp.RuntimeBinder.Semantics @@ -75,6 +76,7 @@ public AggregateType(AggregateSymbol parent, TypeArray typeArgsThis, AggregateTy public AggregateType BaseClass { + [RequiresUnreferencedCode(Binder.TrimmerWarning)] get { if (_baseType == null) @@ -110,6 +112,7 @@ public AggregateType BaseClass public IEnumerable TypeHierarchy { + [RequiresUnreferencedCode(Binder.TrimmerWarning)] get { if (IsInterfaceType) @@ -120,11 +123,11 @@ public IEnumerable TypeHierarchy yield return iface; } - yield return PredefinedTypes.GetPredefinedAggregate(PredefinedType.PT_OBJECT).getThisType(); + yield return GetPredefinedAggregateGetThisTypeWithSuppressedMessage(); } else { - for (AggregateType agg = this; agg != null; agg = agg.BaseClass) + for (AggregateType agg = this; agg != null; agg = agg.BaseClassWithSuppressedMessage) { yield return agg; } @@ -132,6 +135,20 @@ public IEnumerable TypeHierarchy } } + private AggregateType BaseClassWithSuppressedMessage + { + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "Workarounds https://github.com/mono/linker/issues/1906. All usages are marked as unsafe.")] + get => BaseClass; + } + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "Workarounds https://github.com/mono/linker/issues/1906. All usages are marked as unsafe.")] + private static AggregateType GetPredefinedAggregateGetThisTypeWithSuppressedMessage() + { + return PredefinedTypes.GetPredefinedAggregate(PredefinedType.PT_OBJECT).getThisType(); + } + public TypeArray TypeArgsThis { get; } public TypeArray TypeArgsAll { get; } @@ -140,6 +157,7 @@ public IEnumerable TypeHierarchy private bool IsCollectionType { + [RequiresUnreferencedCode(Binder.TrimmerWarning)] get { Type sysType = AssociatedSystemType; @@ -165,6 +183,7 @@ private bool IsCollectionType public TypeArray WinRTCollectionIfacesAll { + [RequiresUnreferencedCode(Binder.TrimmerWarning)] get { if (_winrtifacesAll == null) @@ -281,8 +300,13 @@ public override AggregateType UnderlyingEnumType } } - public override Type AssociatedSystemType => _associatedSystemType ?? (_associatedSystemType = CalculateAssociatedSystemType()); + public override Type AssociatedSystemType + { + [RequiresUnreferencedCode(Binder.TrimmerWarning)] + get => _associatedSystemType ?? (_associatedSystemType = CalculateAssociatedSystemType()); + } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private Type CalculateAssociatedSystemType() { Type uninstantiatedType = OwningAggregate.AssociatedSystemType; @@ -385,6 +409,7 @@ public override ConstValKind ConstValKind } } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public override AggregateType GetAts() => this; } } diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/ArrayType.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/ArrayType.cs index ddd5b04e06d824..e4a81400708a87 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/ArrayType.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/ArrayType.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics.CodeAnalysis; using Microsoft.CSharp.RuntimeBinder.Syntax; namespace Microsoft.CSharp.RuntimeBinder.Semantics @@ -47,6 +48,7 @@ public CType BaseElementType public override Type AssociatedSystemType { + [RequiresUnreferencedCode(Binder.TrimmerWarning)] get { Type elementType = ElementType.AssociatedSystemType; @@ -60,6 +62,7 @@ public override Type AssociatedSystemType public override ConstValKind ConstValKind => ConstValKind.IntPtr; + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public override AggregateType GetAts() => SymbolLoader.GetPredefindType(PredefinedType.PT_ARRAY); } } diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/NullableType.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/NullableType.cs index 749ea49018d195..e3ed75d96e9904 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/NullableType.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/NullableType.cs @@ -25,6 +25,7 @@ public NullableType(CType underlyingType) UnderlyingType = underlyingType; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public override AggregateType GetAts() => _ats ?? (_ats = TypeManager.GetAggregate(TypeManager.GetNullable(), TypeArray.Allocate(UnderlyingType))); @@ -44,7 +45,11 @@ public override CType StripNubs(out bool wasNullable) public override bool IsStructType => true; - public override Type AssociatedSystemType => typeof(Nullable<>).MakeGenericType(UnderlyingType.AssociatedSystemType); + public override Type AssociatedSystemType + { + [RequiresUnreferencedCode(Binder.TrimmerWarning)] + get => typeof(Nullable<>).MakeGenericType(UnderlyingType.AssociatedSystemType); + } public override CType BaseOrParameterOrElementType => UnderlyingType; diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/ParameterModifierType.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/ParameterModifierType.cs index 655efd160abc29..fdb144ea2fe5f6 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/ParameterModifierType.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/ParameterModifierType.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics.CodeAnalysis; namespace Microsoft.CSharp.RuntimeBinder.Semantics { @@ -27,7 +28,11 @@ public ParameterModifierType(CType parameterType, bool isOut) public CType ParameterType { get; } - public override Type AssociatedSystemType => ParameterType.AssociatedSystemType.MakeByRefType(); + public override Type AssociatedSystemType + { + [RequiresUnreferencedCode(Binder.TrimmerWarning)] + get => ParameterType.AssociatedSystemType.MakeByRefType(); + } public override CType BaseOrParameterOrElementType => ParameterType; } diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/PointerType.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/PointerType.cs index a216a85f252995..2af60e2197a0a1 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/PointerType.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/PointerType.cs @@ -19,7 +19,11 @@ public PointerType(CType referentType) public override bool IsUnsafe() => true; - public override Type AssociatedSystemType => ReferentType.AssociatedSystemType.MakePointerType(); + public override Type AssociatedSystemType + { + [RequiresUnreferencedCode(Binder.TrimmerWarning)] + get => ReferentType.AssociatedSystemType.MakePointerType(); + } public override CType BaseOrParameterOrElementType => ReferentType; diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/PredefinedTypes.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/PredefinedTypes.cs index d8e0b2fc1d92d1..291a1fa7990d9b 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/PredefinedTypes.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/PredefinedTypes.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Microsoft.CSharp.RuntimeBinder.Syntax; namespace Microsoft.CSharp.RuntimeBinder.Semantics @@ -13,6 +14,7 @@ internal static class PredefinedTypes private static readonly AggregateSymbol[] s_predefSymbols = new AggregateSymbol[(int)PredefinedType.PT_COUNT]; // We want to delay load the predefined symbols as needed. + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static AggregateSymbol DelayLoadPredefSym(PredefinedType pt) { AggregateType type = (AggregateType)SymbolTable.GetCTypeFromType(PredefinedTypeFacts.GetAssociatedSystemType(pt)); @@ -29,6 +31,7 @@ internal static AggregateSymbol InitializePredefinedType(AggregateSymbol sym, Pr return sym; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static AggregateSymbol GetPredefinedAggregate(PredefinedType pt) => s_predefSymbols[(int)pt] ?? (s_predefSymbols[(int)pt] = DelayLoadPredefSym(pt)); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/Type.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/Type.cs index 6023fd80ab1990..9bcc90478d2ea1 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/Type.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/Type.cs @@ -17,7 +17,11 @@ private protected CType(TypeKind kind) } [ExcludeFromCodeCoverage(Justification = "Should only be called through override")] - public virtual Type AssociatedSystemType => throw Error.InternalCompilerError(); + public virtual Type AssociatedSystemType + { + [RequiresUnreferencedCode(Binder.TrimmerWarning)] + get => throw Error.InternalCompilerError(); + } public TypeKind TypeKind { get; } @@ -122,6 +126,7 @@ public virtual CType StripNubs(out bool wasNullable) public virtual bool IsReferenceType => false; [ExcludeFromCodeCoverage(Justification = "Should only be called through override")] + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public virtual AggregateType GetAts() { Debug.Fail("Bad type for AsAggregateType"); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/TypeManager.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/TypeManager.cs index d77a6e12d9dc03..539221af9cdc40 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/TypeManager.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/TypeManager.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.CompilerServices; using System.Security; @@ -178,6 +179,7 @@ public static ParameterModifierType GetParameterModifier(CType paramType, bool i return pParamModifier; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static AggregateSymbol GetNullable() => GetPredefAgg(PredefinedType.PT_G_OPTIONAL); private static CType SubstType(CType typeSrc, TypeArray typeArgsCls, TypeArray typeArgsMeth, bool denormMeth) @@ -544,6 +546,7 @@ public static bool TypeContainsTyVars(CType type, TypeArray typeVars) } } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public static AggregateSymbol GetPredefAgg(PredefinedType pt) => PredefinedTypes.GetPredefinedAggregate(pt); public static AggregateType SubstType(AggregateType typeSrc, SubstContext ctx) => @@ -583,6 +586,7 @@ public static TypeParameterType GetTypeParameter(TypeParameterSymbol pSymbol) // RUNTIME BINDER ONLY CHANGE // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal static CType GetBestAccessibleType(AggregateSymbol context, CType typeSrc) { // This method implements the "best accessible type" algorithm for determining the type @@ -659,6 +663,7 @@ internal static CType GetBestAccessibleType(AggregateSymbol context, CType typeS return GetPredefAgg(PredefinedType.PT_VALUE).getThisType(); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static bool TryVarianceAdjustmentToGetAccessibleType(AggregateSymbol context, AggregateType typeSrc, out CType typeDst) { Debug.Assert(typeSrc != null); @@ -719,6 +724,7 @@ private static bool TryVarianceAdjustmentToGetAccessibleType(AggregateSymbol con return true; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static bool TryArrayVarianceAdjustmentToGetAccessibleType(AggregateSymbol context, ArrayType typeSrc, out CType typeDst) { Debug.Assert(typeSrc != null); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/TypeParameterType.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/TypeParameterType.cs index 51c71edee567b7..9dd7de1f3d4f61 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/TypeParameterType.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/TypeParameterType.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using Microsoft.CSharp.RuntimeBinder.Syntax; @@ -54,11 +55,15 @@ public TypeParameterType(TypeParameterSymbol symbol) public TypeArray Bounds => Symbol.GetBounds(); - public override Type AssociatedSystemType => - (IsMethodTypeParameter - ? ((MethodInfo)((MethodSymbol)OwningSymbol).AssociatedMemberInfo).GetGenericArguments() - : ((AggregateSymbol)OwningSymbol).AssociatedSystemType.GetGenericArguments() - )[IndexInOwnParameters]; + public override Type AssociatedSystemType + { + [RequiresUnreferencedCode(Binder.TrimmerWarning)] + get => + (IsMethodTypeParameter + ? ((MethodInfo)((MethodSymbol)OwningSymbol).AssociatedMemberInfo).GetGenericArguments() + : ((AggregateSymbol)OwningSymbol).AssociatedSystemType.GetGenericArguments() + )[IndexInOwnParameters]; + } public override FUNDTYPE FundamentalType => FUNDTYPE.FT_VAR; } diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/UnaOpSig.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/UnaOpSig.cs index ea31650cb3ebb9..37db7d2722701f 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/UnaOpSig.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/UnaOpSig.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Microsoft.CSharp.RuntimeBinder.Syntax; namespace Microsoft.CSharp.RuntimeBinder.Semantics @@ -49,6 +50,7 @@ public UnaOpFullSig(CType type, PfnBindUnaOp pfn, LiftFlags grflt, UnaOpFuncKind Set the values of the UnaOpFullSig from the given UnaOpSig. The ExpressionBinder is needed to get the predefined type. Returns true iff the predef type is found. ***************************************************************************************************/ + [RequiresUnreferencedCode(Binder.TrimmerWarning)] public UnaOpFullSig(ExpressionBinder fnc, UnaOpSig uos) { this.pt = uos.pt; diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/SymbolTable.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/SymbolTable.cs index a742cc7740ffb5..c2056e5b0d2c32 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/SymbolTable.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/SymbolTable.cs @@ -45,6 +45,7 @@ public override bool Equals(object obj) public override int GetHashCode() => Type.GetHashCode() ^ Name.GetHashCode(); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal static void PopulateSymbolTableWithName( string name, IEnumerable typeArguments, @@ -86,6 +87,7 @@ internal static void PopulateSymbolTableWithName( ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal static SymWithType LookupMember( string name, Expr callingObject, @@ -122,6 +124,7 @@ internal static SymWithType LookupMember( return mem.SwtFirst(); } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static void AddParameterConversions(MethodBase method) { foreach (ParameterInfo param in method.GetParameters()) @@ -131,6 +134,7 @@ private static void AddParameterConversions(MethodBase method) } #region InheritanceHierarchy + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static void AddNamesOnType(NameHashKey key) { Debug.Assert(!s_namesLoadedForEachType.Contains(key)); @@ -144,6 +148,7 @@ private static void AddNamesOnType(NameHashKey key) ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static void AddNamesInInheritanceHierarchy(string name, List inheritance) { for (int i = inheritance.Count - 1; i >= 0; --i) @@ -225,6 +230,7 @@ private static void AddNamesInInheritanceHierarchy(string name, List inher ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static List CreateInheritanceHierarchyList(Type type) { List list; @@ -289,6 +295,7 @@ private static Name GetName(Type type) #region TypeParameters ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static TypeArray GetMethodTypeParameters(MethodInfo method, MethodSymbol parent) { if (method.IsGenericMethod) @@ -316,6 +323,7 @@ private static TypeArray GetMethodTypeParameters(MethodInfo method, MethodSymbol ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static TypeArray GetAggregateTypeParameters(Type type, AggregateSymbol agg) { if (type.IsGenericType) @@ -373,6 +381,7 @@ private static TypeArray GetAggregateTypeParameters(Type type, AggregateSymbol a ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static TypeParameterType LoadClassTypeParameter(AggregateSymbol parent, Type t) { for (AggregateSymbol p = parent; p != null; p = p.parent as AggregateSymbol) @@ -486,6 +495,7 @@ private static Type GetOriginalTypeParameterType(Type t) ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static TypeParameterType LoadMethodTypeParameter(MethodSymbol parent, Type t) { for (Symbol sym = parent.firstChild; sym != null; sym = sym.nextChild) @@ -567,6 +577,7 @@ private static TypeParameterType AddTypeParameterToSymbolTable( #region LoadTypeChain ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static CType LoadSymbolsFromType(Type type) { List declarationChain = BuildDeclarationChain(type); @@ -631,6 +642,7 @@ private static CType LoadSymbolsFromType(Type type) ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static TypeParameterType ProcessMethodTypeParameter(MethodInfo methinfo, Type t, AggregateSymbol parent) { MethodSymbol meth = FindMatchingMethod(methinfo, parent); @@ -650,6 +662,7 @@ private static TypeParameterType ProcessMethodTypeParameter(MethodInfo methinfo, ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static CType GetConstructedType(Type type, AggregateSymbol agg) { // We've found the one we want, so return it. @@ -672,7 +685,7 @@ private static CType GetConstructedType(Type type, AggregateSymbol agg) } ///////////////////////////////////////////////////////////////////////////////// - + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static CType ProcessSpecialTypeInChain(NamespaceOrAggregateSymbol parent, Type t) { if (t.IsGenericParameter) @@ -703,6 +716,7 @@ private static CType ProcessSpecialTypeInChain(NamespaceOrAggregateSymbol parent ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static List BuildDeclarationChain(Type callingType) { // We need to build the parent chain of the calling type. Since we only @@ -791,6 +805,7 @@ private static NamespaceSymbol AddNamespaceToSymbolTable(NamespaceOrAggregateSym #region CTypeFromType ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal static CType[] GetCTypeArrayFromTypes(Type[] types) { Debug.Assert(types != null); @@ -813,7 +828,7 @@ internal static CType[] GetCTypeArrayFromTypes(Type[] types) } ///////////////////////////////////////////////////////////////////////////////// - + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal static CType GetCTypeFromType(Type type) => type.IsByRef ? TypeManager.GetParameterModifier(LoadSymbolsFromType(type.GetElementType()), false) : LoadSymbolsFromType(type); @@ -823,6 +838,7 @@ internal static CType GetCTypeFromType(Type type) => type.IsByRef #region Aggregates ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static AggregateSymbol AddAggregateToSymbolTable( NamespaceOrAggregateSymbol parent, Type type) @@ -975,6 +991,7 @@ private static AggregateSymbol AddAggregateToSymbolTable( ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static void SetInterfacesOnAggregate(AggregateSymbol aggregate, Type type) { if (type.IsGenericType) @@ -999,6 +1016,7 @@ private static void SetInterfacesOnAggregate(AggregateSymbol aggregate, Type typ #region Field ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static FieldSymbol AddFieldToSymbolTable(FieldInfo fieldInfo, AggregateSymbol aggregate) { FieldSymbol field = SymbolStore.LookupSym( @@ -1053,6 +1071,7 @@ private static FieldSymbol AddFieldToSymbolTable(FieldInfo fieldInfo, AggregateS ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static void AddEventToSymbolTable(EventInfo eventInfo, AggregateSymbol aggregate, FieldSymbol addedField) { EventSymbol ev = SymbolStore.LookupSym( @@ -1110,6 +1129,7 @@ private static void AddEventToSymbolTable(EventInfo eventInfo, AggregateSymbol a #region Properties ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal static void AddPredefinedPropertyToSymbolTable(AggregateSymbol type, Name property) { AggregateType aggtype = type.getThisType(); @@ -1126,6 +1146,7 @@ internal static void AddPredefinedPropertyToSymbolTable(AggregateSymbol type, Na ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static void AddPropertyToSymbolTable(PropertyInfo property, AggregateSymbol aggregate) { Name name; @@ -1275,6 +1296,7 @@ private static void AddPropertyToSymbolTable(PropertyInfo property, AggregateSym #region Methods ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal static void AddPredefinedMethodToSymbolTable(AggregateSymbol type, Name methodName) { Type t = type.getThisType().AssociatedSystemType; @@ -1309,6 +1331,7 @@ internal static void AddPredefinedMethodToSymbolTable(AggregateSymbol type, Name ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static MethodSymbol AddMethodToSymbolTable(MethodBase member, AggregateSymbol callingAggregate, MethodKindEnum kind) { MethodInfo method = member as MethodInfo; @@ -1410,6 +1433,7 @@ private static MethodSymbol AddMethodToSymbolTable(MethodBase member, AggregateS ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static void SetParameterDataForMethProp(MethodOrPropertySymbol methProp, ParameterInfo[] parameters) { if (parameters.Length > 0) @@ -1433,6 +1457,7 @@ private static void SetParameterDataForMethProp(MethodOrPropertySymbol methProp, ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static void SetParameterAttributes(MethodOrPropertySymbol methProp, ParameterInfo[] parameters, int i) { ParameterInfo parameter = parameters[i]; @@ -1580,6 +1605,7 @@ private static MethodSymbol FindMatchingMethod(MemberInfo method, AggregateSymbo return null; } + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static TypeArray CreateParameterArray(MemberInfo associatedInfo, ParameterInfo[] parameters) { bool isVarArg = associatedInfo is MethodBase mb && (mb.CallingConvention & CallingConventions.VarArgs) != 0; @@ -1600,6 +1626,7 @@ private static TypeArray CreateParameterArray(MemberInfo associatedInfo, Paramet ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static CType GetTypeOfParameter(ParameterInfo p, MemberInfo m) { Type t = p.ParameterType; @@ -1648,6 +1675,7 @@ private static bool DoesMethodHaveParameterArray(ParameterInfo[] parameters) ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static SymWithType GetSlotForOverride(MethodInfo method) { if (method.IsVirtual && method.IsHideBySig) @@ -1675,6 +1703,7 @@ private static SymWithType GetSlotForOverride(MethodInfo method) ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static MethodSymbol FindMethodFromMemberInfo(MemberInfo baseMemberInfo) { CType t = GetCTypeFromType(baseMemberInfo.DeclaringType); @@ -1704,6 +1733,7 @@ internal static bool AggregateContainsMethod(AggregateSymbol agg, string szName, #region Conversions ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] internal static void AddConversionsForType(Type type) { if (type.IsInterface) @@ -1718,6 +1748,7 @@ internal static void AddConversionsForType(Type type) ///////////////////////////////////////////////////////////////////////////////// + [RequiresUnreferencedCode(Binder.TrimmerWarning)] private static void AddConversionsForOneType(Type type) { if (type.IsGenericType) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Abstractions/ref/Microsoft.Extensions.Configuration.Abstractions.cs b/src/libraries/Microsoft.Extensions.Configuration.Abstractions/ref/Microsoft.Extensions.Configuration.Abstractions.cs index c743a37346c22f..6c83ffe225776b 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Abstractions/ref/Microsoft.Extensions.Configuration.Abstractions.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Abstractions/ref/Microsoft.Extensions.Configuration.Abstractions.cs @@ -15,6 +15,12 @@ public static partial class ConfigurationExtensions public static string GetConnectionString(this Microsoft.Extensions.Configuration.IConfiguration configuration, string name) { throw null; } public static Microsoft.Extensions.Configuration.IConfigurationSection GetRequiredSection(this Microsoft.Extensions.Configuration.IConfiguration configuration, string key) { throw null; } } + [System.AttributeUsageAttribute(System.AttributeTargets.Property)] + public sealed partial class ConfigurationKeyNameAttribute : System.Attribute + { + public ConfigurationKeyNameAttribute(string name) { } + public string Name { get { throw null; } } + } public static partial class ConfigurationPath { public static readonly string KeyDelimiter; diff --git a/src/libraries/Microsoft.Extensions.Configuration.Abstractions/src/ConfigurationKeyNameAttribute.cs b/src/libraries/Microsoft.Extensions.Configuration.Abstractions/src/ConfigurationKeyNameAttribute.cs new file mode 100644 index 00000000000000..d9aeb155804f1c --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Configuration.Abstractions/src/ConfigurationKeyNameAttribute.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Microsoft.Extensions.Configuration +{ + [AttributeUsage(AttributeTargets.Property)] + public sealed class ConfigurationKeyNameAttribute : Attribute + { + public ConfigurationKeyNameAttribute(string name) => Name = name; + + public string Name { get; } + } +} diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs index 4d2e5cf4889a9a..33aa11eaed985d 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs @@ -208,7 +208,7 @@ private static void BindProperty(PropertyInfo property, object instance, IConfig return; } - propertyValue = BindInstance(property.PropertyType, propertyValue, config.GetSection(property.Name), options); + propertyValue = GetPropertyValue(property, instance, config, options); if (propertyValue != null && hasSetter) { @@ -575,5 +575,48 @@ private static IEnumerable GetAllProperties(Type type) return allProperties; } + + private static object GetPropertyValue(PropertyInfo property, object instance, IConfiguration config, BinderOptions options) + { + string propertyName = GetPropertyName(property); + return BindInstance( + property.PropertyType, + property.GetValue(instance), + config.GetSection(propertyName), + options); + } + + private static string GetPropertyName(MemberInfo property) + { + if (property == null) + { + throw new ArgumentNullException(nameof(property)); + } + + // Check for a custom property name used for configuration key binding + foreach (var attributeData in property.GetCustomAttributesData()) + { + if (attributeData.AttributeType != typeof(ConfigurationKeyNameAttribute)) + { + continue; + } + + // Ensure ConfigurationKeyName constructor signature matches expectations + if (attributeData.ConstructorArguments.Count != 1) + { + break; + } + + // Assumes ConfigurationKeyName constructor first arg is the string key name + string name = attributeData + .ConstructorArguments[0] + .Value? + .ToString(); + + return !string.IsNullOrWhiteSpace(name) ? name : property.Name; + } + + return property.Name; + } } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/ConfigurationBinderTests.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/ConfigurationBinderTests.cs index 2195e56c596d8a..734bcf3ed4aca5 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/ConfigurationBinderTests.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/ConfigurationBinderTests.cs @@ -34,6 +34,9 @@ public ComplexOptions() internal string InternalProperty { get; set; } protected string ProtectedProperty { get; set; } + [ConfigurationKeyName("Named_Property")] + public string NamedProperty { get; set; } + protected string ProtectedPrivateSet { get; private set; } private string PrivateReadOnly { get; } @@ -201,6 +204,22 @@ public void CanBindIConfigurationSectionWithDerivedOptionsSection() Assert.Null(options.Section.Value); } + [Fact] + public void CanBindConfigurationKeyNameAttributes() + { + var dic = new Dictionary + { + {"Named_Property", "Yo"}, + }; + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddInMemoryCollection(dic); + var config = configurationBuilder.Build(); + + var options = config.Get(); + + Assert.Equal("Yo", options.NamedProperty); + } + [Fact] public void EmptyStringIsNullable() { diff --git a/src/libraries/Microsoft.Extensions.Configuration/tests/FunctionalTests/ConfigurationTests.cs b/src/libraries/Microsoft.Extensions.Configuration/tests/FunctionalTests/ConfigurationTests.cs index 62b2589ad1660f..ac38456fdfb4b7 100644 --- a/src/libraries/Microsoft.Extensions.Configuration/tests/FunctionalTests/ConfigurationTests.cs +++ b/src/libraries/Microsoft.Extensions.Configuration/tests/FunctionalTests/ConfigurationTests.cs @@ -901,7 +901,7 @@ public void GetDefaultBasePathForSources() [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] - [SkipOnMono("System.IO.FileSystem.Watcher is not supported on wasm", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "System.IO.FileSystem.Watcher is not supported on Browser")] public void CanEnumerateProviders() { var config = CreateBuilder() diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngineScope.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngineScope.cs index 656cf9b10c4f1f..b29b4016bc0f5b 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngineScope.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngineScope.cs @@ -176,6 +176,12 @@ private void ClearState() // try to return to the pool while somebody is trying to access ResolvedServices. lock (_scopeLock) { + // Don't attempt to dispose if we're already disposed + if (_state == null) + { + return; + } + // ResolvedServices is never cleared for singletons because there might be a compilation running in background // trying to get a cached singleton service. If it doesn't find it // it will try to create a new one which will result in an ObjectDisposedException. diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderEngineScopeTests.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderEngineScopeTests.cs index ae36f2e1d64bba..223c6bfd7bc8a9 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderEngineScopeTests.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderEngineScopeTests.cs @@ -21,6 +21,16 @@ public void ResolvedServicesAfterDispose_ThrowsObjectDispose() Assert.Throws(() => serviceProviderEngineScope.ResolvedServices); } + [Fact] + public void DoubleDisposeWorks() + { + var engine = new FakeEngine(); + var serviceProviderEngineScope = new ServiceProviderEngineScope(engine); + serviceProviderEngineScope.ResolvedServices.Add(new ServiceCacheKey(typeof(IFakeService), 0), null); + serviceProviderEngineScope.Dispose(); + serviceProviderEngineScope.Dispose(); + } + private class FakeEngine : ServiceProviderEngine { public FakeEngine() : diff --git a/src/libraries/Microsoft.Extensions.DependencyModel/tests/AssemblyInfo.cs b/src/libraries/Microsoft.Extensions.DependencyModel/tests/AssemblyInfo.cs deleted file mode 100644 index 3a74f33d7aa32e..00000000000000 --- a/src/libraries/Microsoft.Extensions.DependencyModel/tests/AssemblyInfo.cs +++ /dev/null @@ -1,7 +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; -using Xunit; - -[assembly: ActiveIssue("https://github.com/dotnet/runtime/issues/37669", TestPlatforms.Browser)] diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/AssemblyInfo.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/AssemblyInfo.cs index 99de9b16c5dfad..b95172c7efbafa 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/AssemblyInfo.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/AssemblyInfo.cs @@ -4,4 +4,4 @@ using System; using Xunit; -[assembly: SkipOnMono("Microsoft.Extensions.FileProviders.Physical is not supported on wasm", TestPlatforms.Browser)] +[assembly: SkipOnPlatform(TestPlatforms.Browser, "Microsoft.Extensions.FileProviders.Physical is not supported on Browser")] diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/Microsoft.Extensions.FileProviders.Physical.Tests.csproj b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/Microsoft.Extensions.FileProviders.Physical.Tests.csproj index 2f22d08400baa6..d57fed0ca59497 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/Microsoft.Extensions.FileProviders.Physical.Tests.csproj +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/Microsoft.Extensions.FileProviders.Physical.Tests.csproj @@ -4,6 +4,7 @@ Microsoft.Extensions.FileProviders.Physical $(NetCoreAppCurrent);net461 true + true diff --git a/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/AssemblyInfo.cs b/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/AssemblyInfo.cs index 51acee789dcde1..aa3d381acc7547 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/AssemblyInfo.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/AssemblyInfo.cs @@ -5,5 +5,4 @@ using Microsoft.Extensions.Configuration.UserSecrets; using Xunit; -[assembly: ActiveIssue("https://github.com/dotnet/runtime/issues/37669", TestPlatforms.Browser)] [assembly: UserSecretsId("Microsoft.Extensions.Hosting.Unit.Tests")] diff --git a/src/libraries/Microsoft.Extensions.Options/src/OptionsFactory.cs b/src/libraries/Microsoft.Extensions.Options/src/OptionsFactory.cs index f2dcad253fc1c6..f14e2b1e28b1c8 100644 --- a/src/libraries/Microsoft.Extensions.Options/src/OptionsFactory.cs +++ b/src/libraries/Microsoft.Extensions.Options/src/OptionsFactory.cs @@ -25,7 +25,7 @@ public class OptionsFactory<[DynamicallyAccessedMembers(Options.DynamicallyAcces /// /// The configuration actions to run. /// The initialization actions to run. - public OptionsFactory(IEnumerable> setups, IEnumerable> postConfigures) : this(setups, postConfigures, validations: null) + public OptionsFactory(IEnumerable> setups, IEnumerable> postConfigures) : this(setups, postConfigures, validations: Array.Empty>()) { } /// diff --git a/src/libraries/Microsoft.Extensions.Options/tests/Microsoft.Extensions.Options.Tests/OptionsFactoryTests.cs b/src/libraries/Microsoft.Extensions.Options/tests/Microsoft.Extensions.Options.Tests/OptionsFactoryTests.cs index 061388035ebc95..b89752f4ce0dac 100644 --- a/src/libraries/Microsoft.Extensions.Options/tests/Microsoft.Extensions.Options.Tests/OptionsFactoryTests.cs +++ b/src/libraries/Microsoft.Extensions.Options/tests/Microsoft.Extensions.Options.Tests/OptionsFactoryTests.cs @@ -168,6 +168,15 @@ public void CanPostConfigureAllDefaultAndNamedOptions() Assert.Equal("Default1", factory.Create("1").Message); } + [Fact] + public void CanCreateOptionsFactory() + { + var factory = new OptionsFactory(new IConfigureOptions[0], + new IPostConfigureOptions[] { }); + + Assert.Equal("", factory.Create("").Message); + } + public class FakeOptionsSetupA : ConfigureOptions { public FakeOptionsSetupA() : base(o => o.Message += "A") { } diff --git a/src/libraries/Microsoft.NETCore.Platforms/src/runtime.compatibility.json b/src/libraries/Microsoft.NETCore.Platforms/src/runtime.compatibility.json index a740ab7324fbc5..bf3295d6351e2f 100644 --- a/src/libraries/Microsoft.NETCore.Platforms/src/runtime.compatibility.json +++ b/src/libraries/Microsoft.NETCore.Platforms/src/runtime.compatibility.json @@ -2707,8 +2707,6 @@ ], "ios.10": [ "ios.10", - "ios.9", - "ios.8", "ios", "unix", "any", @@ -2717,10 +2715,6 @@ "ios.10-arm": [ "ios.10-arm", "ios.10", - "ios.9-arm", - "ios.9", - "ios.8-arm", - "ios.8", "ios-arm", "ios", "unix-arm", @@ -2731,10 +2725,6 @@ "ios.10-arm64": [ "ios.10-arm64", "ios.10", - "ios.9-arm64", - "ios.9", - "ios.8-arm64", - "ios.8", "ios-arm64", "ios", "unix-arm64", @@ -2745,10 +2735,6 @@ "ios.10-x64": [ "ios.10-x64", "ios.10", - "ios.9-x64", - "ios.9", - "ios.8-x64", - "ios.8", "ios-x64", "ios", "unix-x64", @@ -2759,10 +2745,6 @@ "ios.10-x86": [ "ios.10-x86", "ios.10", - "ios.9-x86", - "ios.9", - "ios.8-x86", - "ios.8", "ios-x86", "ios", "unix-x86", @@ -2773,8 +2755,6 @@ "ios.11": [ "ios.11", "ios.10", - "ios.9", - "ios.8", "ios", "unix", "any", @@ -2785,10 +2765,6 @@ "ios.11", "ios.10-arm64", "ios.10", - "ios.9-arm64", - "ios.9", - "ios.8-arm64", - "ios.8", "ios-arm64", "ios", "unix-arm64", @@ -2801,10 +2777,6 @@ "ios.11", "ios.10-x64", "ios.10", - "ios.9-x64", - "ios.9", - "ios.8-x64", - "ios.8", "ios-x64", "ios", "unix-x64", @@ -2816,8 +2788,6 @@ "ios.12", "ios.11", "ios.10", - "ios.9", - "ios.8", "ios", "unix", "any", @@ -2830,10 +2800,6 @@ "ios.11", "ios.10-arm64", "ios.10", - "ios.9-arm64", - "ios.9", - "ios.8-arm64", - "ios.8", "ios-arm64", "ios", "unix-arm64", @@ -2848,10 +2814,6 @@ "ios.11", "ios.10-x64", "ios.10", - "ios.9-x64", - "ios.9", - "ios.8-x64", - "ios.8", "ios-x64", "ios", "unix-x64", @@ -2864,8 +2826,6 @@ "ios.12", "ios.11", "ios.10", - "ios.9", - "ios.8", "ios", "unix", "any", @@ -2880,10 +2840,6 @@ "ios.11", "ios.10-arm64", "ios.10", - "ios.9-arm64", - "ios.9", - "ios.8-arm64", - "ios.8", "ios-arm64", "ios", "unix-arm64", @@ -2900,10 +2856,6 @@ "ios.11", "ios.10-x64", "ios.10", - "ios.9-x64", - "ios.9", - "ios.8-x64", - "ios.8", "ios-x64", "ios", "unix-x64", @@ -2911,78 +2863,28 @@ "any", "base" ], - "ios.8": [ - "ios.8", - "ios", - "unix", - "any", - "base" - ], - "ios.8-arm": [ - "ios.8-arm", - "ios.8", - "ios-arm", - "ios", - "unix-arm", - "unix", - "any", - "base" - ], - "ios.8-arm64": [ - "ios.8-arm64", - "ios.8", - "ios-arm64", - "ios", - "unix-arm64", - "unix", - "any", - "base" - ], - "ios.8-x64": [ - "ios.8-x64", - "ios.8", - "ios-x64", - "ios", - "unix-x64", - "unix", - "any", - "base" - ], - "ios.8-x86": [ - "ios.8-x86", - "ios.8", - "ios-x86", - "ios", - "unix-x86", - "unix", - "any", - "base" - ], - "ios.9": [ - "ios.9", - "ios.8", - "ios", - "unix", - "any", - "base" - ], - "ios.9-arm": [ - "ios.9-arm", - "ios.9", - "ios.8-arm", - "ios.8", - "ios-arm", + "ios.14": [ + "ios.14", + "ios.13", + "ios.12", + "ios.11", + "ios.10", "ios", - "unix-arm", "unix", "any", "base" ], - "ios.9-arm64": [ - "ios.9-arm64", - "ios.9", - "ios.8-arm64", - "ios.8", + "ios.14-arm64": [ + "ios.14-arm64", + "ios.14", + "ios.13-arm64", + "ios.13", + "ios.12-arm64", + "ios.12", + "ios.11-arm64", + "ios.11", + "ios.10-arm64", + "ios.10", "ios-arm64", "ios", "unix-arm64", @@ -2990,11 +2892,17 @@ "any", "base" ], - "ios.9-x64": [ - "ios.9-x64", - "ios.9", - "ios.8-x64", - "ios.8", + "ios.14-x64": [ + "ios.14-x64", + "ios.14", + "ios.13-x64", + "ios.13", + "ios.12-x64", + "ios.12", + "ios.11-x64", + "ios.11", + "ios.10-x64", + "ios.10", "ios-x64", "ios", "unix-x64", @@ -3002,18 +2910,6 @@ "any", "base" ], - "ios.9-x86": [ - "ios.9-x86", - "ios.9", - "ios.8-x86", - "ios.8", - "ios-x86", - "ios", - "unix-x86", - "unix", - "any", - "base" - ], "iossimulator": [ "iossimulator", "ios", @@ -3053,8 +2949,6 @@ ], "iossimulator.10": [ "iossimulator.10", - "iossimulator.9", - "iossimulator.8", "iossimulator", "ios", "unix", @@ -3064,10 +2958,6 @@ "iossimulator.10-arm64": [ "iossimulator.10-arm64", "iossimulator.10", - "iossimulator.9-arm64", - "iossimulator.9", - "iossimulator.8-arm64", - "iossimulator.8", "iossimulator-arm64", "iossimulator", "ios-arm64", @@ -3080,10 +2970,6 @@ "iossimulator.10-x64": [ "iossimulator.10-x64", "iossimulator.10", - "iossimulator.9-x64", - "iossimulator.9", - "iossimulator.8-x64", - "iossimulator.8", "iossimulator-x64", "iossimulator", "ios-x64", @@ -3096,10 +2982,6 @@ "iossimulator.10-x86": [ "iossimulator.10-x86", "iossimulator.10", - "iossimulator.9-x86", - "iossimulator.9", - "iossimulator.8-x86", - "iossimulator.8", "iossimulator-x86", "iossimulator", "ios-x86", @@ -3112,8 +2994,6 @@ "iossimulator.11": [ "iossimulator.11", "iossimulator.10", - "iossimulator.9", - "iossimulator.8", "iossimulator", "ios", "unix", @@ -3125,10 +3005,6 @@ "iossimulator.11", "iossimulator.10-arm64", "iossimulator.10", - "iossimulator.9-arm64", - "iossimulator.9", - "iossimulator.8-arm64", - "iossimulator.8", "iossimulator-arm64", "iossimulator", "ios-arm64", @@ -3143,10 +3019,6 @@ "iossimulator.11", "iossimulator.10-x64", "iossimulator.10", - "iossimulator.9-x64", - "iossimulator.9", - "iossimulator.8-x64", - "iossimulator.8", "iossimulator-x64", "iossimulator", "ios-x64", @@ -3160,8 +3032,6 @@ "iossimulator.12", "iossimulator.11", "iossimulator.10", - "iossimulator.9", - "iossimulator.8", "iossimulator", "ios", "unix", @@ -3175,10 +3045,6 @@ "iossimulator.11", "iossimulator.10-arm64", "iossimulator.10", - "iossimulator.9-arm64", - "iossimulator.9", - "iossimulator.8-arm64", - "iossimulator.8", "iossimulator-arm64", "iossimulator", "ios-arm64", @@ -3195,10 +3061,6 @@ "iossimulator.11", "iossimulator.10-x64", "iossimulator.10", - "iossimulator.9-x64", - "iossimulator.9", - "iossimulator.8-x64", - "iossimulator.8", "iossimulator-x64", "iossimulator", "ios-x64", @@ -3213,8 +3075,6 @@ "iossimulator.12", "iossimulator.11", "iossimulator.10", - "iossimulator.9", - "iossimulator.8", "iossimulator", "ios", "unix", @@ -3230,10 +3090,6 @@ "iossimulator.11", "iossimulator.10-arm64", "iossimulator.10", - "iossimulator.9-arm64", - "iossimulator.9", - "iossimulator.8-arm64", - "iossimulator.8", "iossimulator-arm64", "iossimulator", "ios-arm64", @@ -3252,42 +3108,6 @@ "iossimulator.11", "iossimulator.10-x64", "iossimulator.10", - "iossimulator.9-x64", - "iossimulator.9", - "iossimulator.8-x64", - "iossimulator.8", - "iossimulator-x64", - "iossimulator", - "ios-x64", - "ios", - "unix-x64", - "unix", - "any", - "base" - ], - "iossimulator.8": [ - "iossimulator.8", - "iossimulator", - "ios", - "unix", - "any", - "base" - ], - "iossimulator.8-arm64": [ - "iossimulator.8-arm64", - "iossimulator.8", - "iossimulator-arm64", - "iossimulator", - "ios-arm64", - "ios", - "unix-arm64", - "unix", - "any", - "base" - ], - "iossimulator.8-x64": [ - "iossimulator.8-x64", - "iossimulator.8", "iossimulator-x64", "iossimulator", "ios-x64", @@ -3297,32 +3117,29 @@ "any", "base" ], - "iossimulator.8-x86": [ - "iossimulator.8-x86", - "iossimulator.8", - "iossimulator-x86", - "iossimulator", - "ios-x86", - "ios", - "unix-x86", - "unix", - "any", - "base" - ], - "iossimulator.9": [ - "iossimulator.9", - "iossimulator.8", + "iossimulator.14": [ + "iossimulator.14", + "iossimulator.13", + "iossimulator.12", + "iossimulator.11", + "iossimulator.10", "iossimulator", "ios", "unix", "any", "base" ], - "iossimulator.9-arm64": [ - "iossimulator.9-arm64", - "iossimulator.9", - "iossimulator.8-arm64", - "iossimulator.8", + "iossimulator.14-arm64": [ + "iossimulator.14-arm64", + "iossimulator.14", + "iossimulator.13-arm64", + "iossimulator.13", + "iossimulator.12-arm64", + "iossimulator.12", + "iossimulator.11-arm64", + "iossimulator.11", + "iossimulator.10-arm64", + "iossimulator.10", "iossimulator-arm64", "iossimulator", "ios-arm64", @@ -3332,11 +3149,17 @@ "any", "base" ], - "iossimulator.9-x64": [ - "iossimulator.9-x64", - "iossimulator.9", - "iossimulator.8-x64", - "iossimulator.8", + "iossimulator.14-x64": [ + "iossimulator.14-x64", + "iossimulator.14", + "iossimulator.13-x64", + "iossimulator.13", + "iossimulator.12-x64", + "iossimulator.12", + "iossimulator.11-x64", + "iossimulator.11", + "iossimulator.10-x64", + "iossimulator.10", "iossimulator-x64", "iossimulator", "ios-x64", @@ -3346,20 +3169,6 @@ "any", "base" ], - "iossimulator.9-x86": [ - "iossimulator.9-x86", - "iossimulator.9", - "iossimulator.8-x86", - "iossimulator.8", - "iossimulator-x86", - "iossimulator", - "ios-x86", - "ios", - "unix-x86", - "unix", - "any", - "base" - ], "linux": [ "linux", "unix", diff --git a/src/libraries/Microsoft.NETCore.Platforms/src/runtime.json b/src/libraries/Microsoft.NETCore.Platforms/src/runtime.json index 9ce5b6033f61e6..ee7d87d932971c 100644 --- a/src/libraries/Microsoft.NETCore.Platforms/src/runtime.json +++ b/src/libraries/Microsoft.NETCore.Platforms/src/runtime.json @@ -1116,31 +1116,31 @@ }, "ios.10": { "#import": [ - "ios.9" + "ios" ] }, "ios.10-arm": { "#import": [ "ios.10", - "ios.9-arm" + "ios-arm" ] }, "ios.10-arm64": { "#import": [ "ios.10", - "ios.9-arm64" + "ios-arm64" ] }, "ios.10-x64": { "#import": [ "ios.10", - "ios.9-x64" + "ios-x64" ] }, "ios.10-x86": { "#import": [ "ios.10", - "ios.9-x86" + "ios-x86" ] }, "ios.11": { @@ -1194,62 +1194,21 @@ "ios.12-x64" ] }, - "ios.8": { - "#import": [ - "ios" - ] - }, - "ios.8-arm": { - "#import": [ - "ios.8", - "ios-arm" - ] - }, - "ios.8-arm64": { - "#import": [ - "ios.8", - "ios-arm64" - ] - }, - "ios.8-x64": { - "#import": [ - "ios.8", - "ios-x64" - ] - }, - "ios.8-x86": { - "#import": [ - "ios.8", - "ios-x86" - ] - }, - "ios.9": { - "#import": [ - "ios.8" - ] - }, - "ios.9-arm": { - "#import": [ - "ios.9", - "ios.8-arm" - ] - }, - "ios.9-arm64": { + "ios.14": { "#import": [ - "ios.9", - "ios.8-arm64" + "ios.13" ] }, - "ios.9-x64": { + "ios.14-arm64": { "#import": [ - "ios.9", - "ios.8-x64" + "ios.14", + "ios.13-arm64" ] }, - "ios.9-x86": { + "ios.14-x64": { "#import": [ - "ios.9", - "ios.8-x86" + "ios.14", + "ios.13-x64" ] }, "iossimulator": { @@ -1277,25 +1236,25 @@ }, "iossimulator.10": { "#import": [ - "iossimulator.9" + "iossimulator" ] }, "iossimulator.10-arm64": { "#import": [ "iossimulator.10", - "iossimulator.9-arm64" + "iossimulator-arm64" ] }, "iossimulator.10-x64": { "#import": [ "iossimulator.10", - "iossimulator.9-x64" + "iossimulator-x64" ] }, "iossimulator.10-x86": { "#import": [ "iossimulator.10", - "iossimulator.9-x86" + "iossimulator-x86" ] }, "iossimulator.11": { @@ -1349,50 +1308,21 @@ "iossimulator.12-x64" ] }, - "iossimulator.8": { - "#import": [ - "iossimulator" - ] - }, - "iossimulator.8-arm64": { - "#import": [ - "iossimulator.8", - "iossimulator-arm64" - ] - }, - "iossimulator.8-x64": { - "#import": [ - "iossimulator.8", - "iossimulator-x64" - ] - }, - "iossimulator.8-x86": { - "#import": [ - "iossimulator.8", - "iossimulator-x86" - ] - }, - "iossimulator.9": { - "#import": [ - "iossimulator.8" - ] - }, - "iossimulator.9-arm64": { + "iossimulator.14": { "#import": [ - "iossimulator.9", - "iossimulator.8-arm64" + "iossimulator.13" ] }, - "iossimulator.9-x64": { + "iossimulator.14-arm64": { "#import": [ - "iossimulator.9", - "iossimulator.8-x64" + "iossimulator.14", + "iossimulator.13-arm64" ] }, - "iossimulator.9-x86": { + "iossimulator.14-x64": { "#import": [ - "iossimulator.9", - "iossimulator.8-x86" + "iossimulator.14", + "iossimulator.13-x64" ] }, "linux": { diff --git a/src/libraries/Microsoft.NETCore.Platforms/src/runtimeGroups.props b/src/libraries/Microsoft.NETCore.Platforms/src/runtimeGroups.props index 75f41db11693e4..16394756f92db7 100644 --- a/src/libraries/Microsoft.NETCore.Platforms/src/runtimeGroups.props +++ b/src/libraries/Microsoft.NETCore.Platforms/src/runtimeGroups.props @@ -83,23 +83,23 @@ unix arm;x86 - 8;9;10 + 10 unix arm64;x64 - 8;9;10;11;12;13 + 10;11;12;13;14 ios x86 - 8;9;10 + 10 ios arm64;x64 - 8;9;10;11;12;13 + 10;11;12;13;14 diff --git a/src/libraries/Microsoft.NETCore.Platforms/tests/AssemblyInfo.cs b/src/libraries/Microsoft.NETCore.Platforms/tests/AssemblyInfo.cs index baad52408c410d..f5a33895866d57 100644 --- a/src/libraries/Microsoft.NETCore.Platforms/tests/AssemblyInfo.cs +++ b/src/libraries/Microsoft.NETCore.Platforms/tests/AssemblyInfo.cs @@ -3,4 +3,4 @@ using Xunit; -[assembly: SkipOnMono("MSBuild is not supported on Browser", TestPlatforms.Browser)] \ No newline at end of file +[assembly: SkipOnPlatform(TestPlatforms.Browser, "MSBuild is not supported on Browser")] \ No newline at end of file diff --git a/src/libraries/Microsoft.XmlSerializer.Generator/pkg/Microsoft.XmlSerializer.Generator.pkgproj b/src/libraries/Microsoft.XmlSerializer.Generator/pkg/Microsoft.XmlSerializer.Generator.pkgproj index 8d86c1f65763fc..9dacf5487dad23 100644 --- a/src/libraries/Microsoft.XmlSerializer.Generator/pkg/Microsoft.XmlSerializer.Generator.pkgproj +++ b/src/libraries/Microsoft.XmlSerializer.Generator/pkg/Microsoft.XmlSerializer.Generator.pkgproj @@ -4,8 +4,8 @@ - https://go.microsoft.com/fwlink/?LinkID=863421 - https://go.microsoft.com/fwlink/?linkid=858594 + https://go.microsoft.com/fwlink/?LinkID=863421 + https://go.microsoft.com/fwlink/?linkid=858594 true false @@ -20,4 +20,4 @@ - \ No newline at end of file + diff --git a/src/libraries/Native/Unix/Common/pal_ssl_types.h b/src/libraries/Native/Unix/Common/pal_ssl_types.h new file mode 100644 index 00000000000000..67e8b6e382ba0c --- /dev/null +++ b/src/libraries/Native/Unix/Common/pal_ssl_types.h @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#pragma once + +#include + +// Matches managed System.Security.Authentication.SslProtocols +enum +{ + PAL_SslProtocol_None = 0, + PAL_SslProtocol_Ssl2 = 12, + PAL_SslProtocol_Ssl3 = 48, + PAL_SslProtocol_Tls10 = 192, + PAL_SslProtocol_Tls11 = 768, + PAL_SslProtocol_Tls12 = 3072, + PAL_SslProtocol_Tls13 = 12288, +}; +typedef int32_t PAL_SslProtocol; diff --git a/src/libraries/Native/Unix/System.Native/CMakeLists.txt b/src/libraries/Native/Unix/System.Native/CMakeLists.txt index 12245f802024fb..d21e27a561b0f4 100644 --- a/src/libraries/Native/Unix/System.Native/CMakeLists.txt +++ b/src/libraries/Native/Unix/System.Native/CMakeLists.txt @@ -47,6 +47,14 @@ else () pal_log.c) endif () +if (CLR_CMAKE_TARGET_MACCATALYST) + set(NATIVE_SOURCES ${NATIVE_SOURCES} + pal_iossupportversion.m) +else () + list (APPEND NATIVE_SOURCES + pal_iossupportversion.c) +endif () + if (NOT CLR_CMAKE_TARGET_BROWSER) list (APPEND NATIVE_SOURCES pal_networkchange.c) endif () diff --git a/src/libraries/Native/Unix/System.Native/entrypoints.c b/src/libraries/Native/Unix/System.Native/entrypoints.c index 5abcf2a91074dd..53e30315ea0c19 100644 --- a/src/libraries/Native/Unix/System.Native/entrypoints.c +++ b/src/libraries/Native/Unix/System.Native/entrypoints.c @@ -10,6 +10,7 @@ #include "pal_errno.h" #include "pal_interfaceaddresses.h" #include "pal_io.h" +#include "pal_iossupportversion.h" #include "pal_log.h" #include "pal_memory.h" #include "pal_mount.h" @@ -237,6 +238,7 @@ static const Entry s_sysNative[] = DllImportEntry(SystemNative_LowLevelMonitor_Create) DllImportEntry(SystemNative_CreateAutoreleasePool) DllImportEntry(SystemNative_DrainAutoreleasePool) + DllImportEntry(SystemNative_iOSSupportVersion) }; EXTERN_C const void* SystemResolveDllImport(const char* name); diff --git a/src/libraries/Native/Unix/System.Native/pal_autoreleasepool.h b/src/libraries/Native/Unix/System.Native/pal_autoreleasepool.h index 75d367084666f1..450b51822ffdb4 100644 --- a/src/libraries/Native/Unix/System.Native/pal_autoreleasepool.h +++ b/src/libraries/Native/Unix/System.Native/pal_autoreleasepool.h @@ -15,3 +15,9 @@ PALEXPORT void* SystemNative_CreateAutoreleasePool(void); * Drains and releases a pool created by SystemNative_CreateAutoreleasePool. */ PALEXPORT void SystemNative_DrainAutoreleasePool(void* pool); + +/** + * Ensure that NSThread is in multi-threading mode when POSIX APIs are used to + * start new threads. + */ +void EnsureNSThreadIsMultiThreaded(void); diff --git a/src/libraries/Native/Unix/System.Native/pal_autoreleasepool.m b/src/libraries/Native/Unix/System.Native/pal_autoreleasepool.m index 77030b07bf381c..e5ebd4d247c599 100644 --- a/src/libraries/Native/Unix/System.Native/pal_autoreleasepool.m +++ b/src/libraries/Native/Unix/System.Native/pal_autoreleasepool.m @@ -15,7 +15,7 @@ - (void)noop:(id)_ } @end -void* SystemNative_CreateAutoreleasePool(void) +void EnsureNSThreadIsMultiThreaded(void) { if (![NSThread isMultiThreaded]) { @@ -30,7 +30,11 @@ - (void)noop:(id)_ [NSThread detachNewThreadSelector:@selector(noop:) toTarget:placeholderObject withObject:nil]; } assert([NSThread isMultiThreaded]); +} +void* SystemNative_CreateAutoreleasePool(void) +{ + EnsureNSThreadIsMultiThreaded(); return [[NSAutoreleasePool alloc] init]; } diff --git a/src/libraries/Native/Unix/System.Native/pal_iossupportversion.c b/src/libraries/Native/Unix/System.Native/pal_iossupportversion.c new file mode 100644 index 00000000000000..f338c8ff3acc06 --- /dev/null +++ b/src/libraries/Native/Unix/System.Native/pal_iossupportversion.c @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "pal_iossupportversion.h" +#include +#include "pal_utilities.h" + +// These functions should not be used, but they need to be defined +// to satisfy the tooling we used to enable redirecting P/Invokes +// for the single file scenario. +const char* SystemNative_iOSSupportVersion(void) +{ + assert_err(false, "iOS support not available on this platform.", EINVAL); + return NULL; +} diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/tests/TestWithConfigSwitches/AssemblyInfo.cs b/src/libraries/Native/Unix/System.Native/pal_iossupportversion.h similarity index 53% rename from src/libraries/System.Diagnostics.DiagnosticSource/tests/TestWithConfigSwitches/AssemblyInfo.cs rename to src/libraries/Native/Unix/System.Native/pal_iossupportversion.h index 7fe77fb2043e78..06ca3e1a666f0b 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/tests/TestWithConfigSwitches/AssemblyInfo.cs +++ b/src/libraries/Native/Unix/System.Native/pal_iossupportversion.h @@ -1,6 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Xunit; +#pragma once -[assembly: ActiveIssue("https://github.com/dotnet/runtime/issues/38433", TestPlatforms.Browser)] \ No newline at end of file +#include "pal_compiler.h" +#include "pal_types.h" + +PALEXPORT const char* SystemNative_iOSSupportVersion(void); diff --git a/src/libraries/Native/Unix/System.Native/pal_iossupportversion.m b/src/libraries/Native/Unix/System.Native/pal_iossupportversion.m new file mode 100644 index 00000000000000..8199b9822e84ec --- /dev/null +++ b/src/libraries/Native/Unix/System.Native/pal_iossupportversion.m @@ -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. + +#include "pal_iossupportversion.h" +#include "pal_autoreleasepool.h" +#import + +const char* SystemNative_iOSSupportVersion() +{ + EnsureNSThreadIsMultiThreaded(); + + @autoreleasepool + { + NSDictionary *plist = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]; + NSString *iOSSupportVersion = (NSString *)[plist objectForKey:@"iOSSupportVersion"]; + return strdup([iOSSupportVersion UTF8String]); + } +} diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_jni.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_jni.c index d166c23dd86456..280ef1dfbabd02 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_jni.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_jni.c @@ -15,6 +15,10 @@ jmethodID g_ByteArrayInputStreamReset; jclass g_Enum; jmethodID g_EnumOrdinal; +// java/lang/String +jclass g_String; +jmethodID g_StringGetBytes; + // java/lang/Throwable jclass g_ThrowableClass; jmethodID g_ThrowableGetCause; @@ -74,8 +78,10 @@ jmethodID g_bitLengthMethod; jmethodID g_sigNumMethod; // javax/net/ssl/SSLParameters -jclass g_sslParamsClass; -jmethodID g_sslParamsGetProtocolsMethod; +jclass g_SSLParametersClass; +jmethodID g_SSLParametersCtor; +jmethodID g_SSLParametersGetProtocols; +jmethodID g_SSLParametersSetServerNames; // javax/net/ssl/SSLContext jclass g_sslCtxClass; @@ -115,6 +121,7 @@ jmethodID g_keyPairGenGenKeyPairMethod; // java/security/KeyStore jclass g_KeyStoreClass; +jmethodID g_KeyStoreGetDefaultType; jmethodID g_KeyStoreGetInstance; jmethodID g_KeyStoreAliases; jmethodID g_KeyStoreContainsAlias; @@ -323,6 +330,10 @@ jmethodID g_EllipticCurveGetB; jmethodID g_EllipticCurveGetField; jmethodID g_EllipticCurveGetSeed; +// java/security/spec/PKCS8EncodedKeySpec +jclass g_PKCS8EncodedKeySpec; +jmethodID g_PKCS8EncodedKeySpecCtor; + // java/security/spec/X509EncodedKeySpec jclass g_X509EncodedKeySpecClass; jmethodID g_X509EncodedKeySpecCtor; @@ -366,49 +377,70 @@ jmethodID g_IteratorNext; jclass g_ListClass; jmethodID g_ListGet; +// javax/net/ssl/HostnameVerifier +jclass g_HostnameVerifier; +jmethodID g_HostnameVerifierVerify; + +// javax/net/ssl/HttpsURLConnection +jclass g_HttpsURLConnection; +jmethodID g_HttpsURLConnectionGetDefaultHostnameVerifier; + +// javax/net/ssl/KeyManagerFactory +jclass g_KeyManagerFactory; +jmethodID g_KeyManagerFactoryGetInstance; +jmethodID g_KeyManagerFactoryInit; +jmethodID g_KeyManagerFactoryGetKeyManagers; + +// javax/net/ssl/SNIHostName +jclass g_SNIHostName; +jmethodID g_SNIHostNameCtor; + // javax/net/ssl/SSLEngine jclass g_SSLEngine; -jmethodID g_SSLEngineSetUseClientModeMethod; -jmethodID g_SSLEngineGetSessionMethod; -jmethodID g_SSLEngineBeginHandshakeMethod; -jmethodID g_SSLEngineWrapMethod; -jmethodID g_SSLEngineUnwrapMethod; -jmethodID g_SSLEngineCloseInboundMethod; -jmethodID g_SSLEngineCloseOutboundMethod; -jmethodID g_SSLEngineGetHandshakeStatusMethod; +jmethodID g_SSLEngineGetApplicationProtocol; +jmethodID g_SSLEngineSetUseClientMode; +jmethodID g_SSLEngineGetSession; +jmethodID g_SSLEngineBeginHandshake; +jmethodID g_SSLEngineWrap; +jmethodID g_SSLEngineUnwrap; +jmethodID g_SSLEngineCloseOutbound; +jmethodID g_SSLEngineGetHandshakeStatus; +jmethodID g_SSLEngineGetSupportedProtocols; +jmethodID g_SSLEngineSetEnabledProtocols; +jmethodID g_SSLEngineSetSSLParameters; // java/nio/ByteBuffer jclass g_ByteBuffer; -jmethodID g_ByteBufferAllocateMethod; -jmethodID g_ByteBufferPutMethod; -jmethodID g_ByteBufferPut2Method; -jmethodID g_ByteBufferPut3Method; -jmethodID g_ByteBufferFlipMethod; -jmethodID g_ByteBufferGetMethod; -jmethodID g_ByteBufferPutBufferMethod; -jmethodID g_ByteBufferLimitMethod; -jmethodID g_ByteBufferRemainingMethod; -jmethodID g_ByteBufferCompactMethod; -jmethodID g_ByteBufferPositionMethod; +jmethodID g_ByteBufferAllocate; +jmethodID g_ByteBufferCompact; +jmethodID g_ByteBufferFlip; +jmethodID g_ByteBufferGet; +jmethodID g_ByteBufferLimit; +jmethodID g_ByteBufferPosition; +jmethodID g_ByteBufferPutBuffer; +jmethodID g_ByteBufferPutByteArray; +jmethodID g_ByteBufferPutByteArrayWithLength; +jmethodID g_ByteBufferRemaining; // javax/net/ssl/SSLContext jclass g_SSLContext; +jmethodID g_SSLContextGetDefault; jmethodID g_SSLContextGetInstanceMethod; jmethodID g_SSLContextInitMethod; jmethodID g_SSLContextCreateSSLEngineMethod; // javax/net/ssl/SSLSession jclass g_SSLSession; -jmethodID g_SSLSessionGetApplicationBufferSizeMethod; -jmethodID g_SSLSessionGetPacketBufferSizeMethod; +jmethodID g_SSLSessionGetApplicationBufferSize; +jmethodID g_SSLSessionGetCipherSuite; +jmethodID g_SSLSessionGetPacketBufferSize; +jmethodID g_SSLSessionGetPeerCertificates; +jmethodID g_SSLSessionGetProtocol; // javax/net/ssl/SSLEngineResult jclass g_SSLEngineResult; -jmethodID g_SSLEngineResultGetStatusMethod; -jmethodID g_SSLEngineResultGetHandshakeStatusMethod; - -// javax/net/ssl/TrustManager -jclass g_TrustManager; +jmethodID g_SSLEngineResultGetStatus; +jmethodID g_SSLEngineResultGetHandshakeStatus; // javax/crypto/KeyAgreement jclass g_KeyAgreementClass; @@ -521,11 +553,6 @@ bool TryGetJNIException(JNIEnv* env, jthrowable *ex, bool printException) return true; } -void AssertOnJNIExceptions(JNIEnv* env) -{ - assert(!CheckJNIExceptions(env)); -} - void SaveTo(uint8_t* src, uint8_t** dst, size_t len, bool overwrite) { assert(overwrite || !(*dst)); @@ -628,6 +655,9 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_Enum = GetClassGRef(env, "java/lang/Enum"); g_EnumOrdinal = GetMethod(env, false, g_Enum, "ordinal", "()I"); + g_String = GetClassGRef(env, "java/lang/String"); + g_StringGetBytes = GetMethod(env, false, g_String, "getBytes", "()[B"); + g_ThrowableClass = GetClassGRef(env, "java/lang/Throwable"); g_ThrowableGetCause = GetMethod(env, false, g_ThrowableClass, "getCause", "()Ljava/lang/Throwable;"); g_ThrowableGetMessage = GetMethod(env, false, g_ThrowableClass, "getMessage", "()Ljava/lang/String;"); @@ -681,8 +711,10 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_bitLengthMethod = GetMethod(env, false, g_bigNumClass, "bitLength", "()I"); g_sigNumMethod = GetMethod(env, false, g_bigNumClass, "signum", "()I"); - g_sslParamsClass = GetClassGRef(env, "javax/net/ssl/SSLParameters"); - g_sslParamsGetProtocolsMethod = GetMethod(env, false, g_sslParamsClass, "getProtocols", "()[Ljava/lang/String;"); + g_SSLParametersClass = GetClassGRef(env, "javax/net/ssl/SSLParameters"); + g_SSLParametersCtor = GetMethod(env, false, g_SSLParametersClass, "", "()V"); + g_SSLParametersGetProtocols = GetMethod(env, false, g_SSLParametersClass, "getProtocols", "()[Ljava/lang/String;"); + g_SSLParametersSetServerNames = GetMethod(env, false, g_SSLParametersClass, "setServerNames", "(Ljava/util/List;)V"); g_sslCtxClass = GetClassGRef(env, "javax/net/ssl/SSLContext"); g_sslCtxGetDefaultMethod = GetMethod(env, true, g_sslCtxClass, "getDefault", "()Ljavax/net/ssl/SSLContext;"); @@ -778,6 +810,7 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_keyPairGenGenKeyPairMethod = GetMethod(env, false, g_keyPairGenClass, "genKeyPair", "()Ljava/security/KeyPair;"); g_KeyStoreClass = GetClassGRef(env, "java/security/KeyStore"); + g_KeyStoreGetDefaultType = GetMethod(env, true, g_KeyStoreClass, "getDefaultType", "()Ljava/lang/String;"); g_KeyStoreGetInstance = GetMethod(env, true, g_KeyStoreClass, "getInstance", "(Ljava/lang/String;)Ljava/security/KeyStore;"); g_KeyStoreAliases = GetMethod(env, false, g_KeyStoreClass, "aliases", "()Ljava/util/Enumeration;"); g_KeyStoreContainsAlias = GetMethod(env, false, g_KeyStoreClass, "containsAlias", "(Ljava/lang/String;)Z"); @@ -885,6 +918,9 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_EllipticCurveGetField = GetMethod(env, false, g_EllipticCurveClass, "getField", "()Ljava/security/spec/ECField;"); g_EllipticCurveGetSeed = GetMethod(env, false, g_EllipticCurveClass, "getSeed", "()[B"); + g_PKCS8EncodedKeySpec = GetClassGRef(env, "java/security/spec/PKCS8EncodedKeySpec"); + g_PKCS8EncodedKeySpecCtor = GetMethod(env, false, g_PKCS8EncodedKeySpec, "", "([B)V"); + g_X509EncodedKeySpecClass = GetClassGRef(env, "java/security/spec/X509EncodedKeySpec"); g_X509EncodedKeySpecCtor = GetMethod(env, false, g_X509EncodedKeySpecClass, "", "([B)V"); @@ -919,43 +955,61 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_ListClass = GetClassGRef(env, "java/util/List"); g_ListGet = GetMethod(env, false, g_ListClass, "get", "(I)Ljava/lang/Object;"); - g_SSLEngine = GetClassGRef(env, "javax/net/ssl/SSLEngine"); - g_SSLEngineSetUseClientModeMethod = GetMethod(env, false, g_SSLEngine, "setUseClientMode", "(Z)V"); - g_SSLEngineGetSessionMethod = GetMethod(env, false, g_SSLEngine, "getSession", "()Ljavax/net/ssl/SSLSession;"); - g_SSLEngineBeginHandshakeMethod = GetMethod(env, false, g_SSLEngine, "beginHandshake", "()V"); - g_SSLEngineWrapMethod = GetMethod(env, false, g_SSLEngine, "wrap", "(Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;)Ljavax/net/ssl/SSLEngineResult;"); - g_SSLEngineUnwrapMethod = GetMethod(env, false, g_SSLEngine, "unwrap", "(Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;)Ljavax/net/ssl/SSLEngineResult;"); - g_SSLEngineGetHandshakeStatusMethod = GetMethod(env, false, g_SSLEngine, "getHandshakeStatus", "()Ljavax/net/ssl/SSLEngineResult$HandshakeStatus;"); - g_SSLEngineCloseInboundMethod = GetMethod(env, false, g_SSLEngine, "closeInbound", "()V"); - g_SSLEngineCloseOutboundMethod = GetMethod(env, false, g_SSLEngine, "closeOutbound", "()V"); - - g_ByteBuffer = GetClassGRef(env, "java/nio/ByteBuffer"); - g_ByteBufferAllocateMethod = GetMethod(env, true, g_ByteBuffer, "allocate", "(I)Ljava/nio/ByteBuffer;"); - g_ByteBufferPutMethod = GetMethod(env, false, g_ByteBuffer, "put", "(Ljava/nio/ByteBuffer;)Ljava/nio/ByteBuffer;"); - g_ByteBufferPut2Method = GetMethod(env, false, g_ByteBuffer, "put", "([B)Ljava/nio/ByteBuffer;"); - g_ByteBufferPut3Method = GetMethod(env, false, g_ByteBuffer, "put", "([BII)Ljava/nio/ByteBuffer;"); - g_ByteBufferFlipMethod = GetMethod(env, false, g_ByteBuffer, "flip", "()Ljava/nio/Buffer;"); - g_ByteBufferLimitMethod = GetMethod(env, false, g_ByteBuffer, "limit", "()I"); - g_ByteBufferGetMethod = GetMethod(env, false, g_ByteBuffer, "get", "([B)Ljava/nio/ByteBuffer;"); - g_ByteBufferPutBufferMethod = GetMethod(env, false, g_ByteBuffer, "put", "(Ljava/nio/ByteBuffer;)Ljava/nio/ByteBuffer;"); - g_ByteBufferRemainingMethod = GetMethod(env, false, g_ByteBuffer, "remaining", "()I"); - g_ByteBufferCompactMethod = GetMethod(env, false, g_ByteBuffer, "compact", "()Ljava/nio/ByteBuffer;"); - g_ByteBufferPositionMethod = GetMethod(env, false, g_ByteBuffer, "position", "()I"); + g_HostnameVerifier = GetClassGRef(env, "javax/net/ssl/HostnameVerifier"); + g_HostnameVerifierVerify = GetMethod(env, false, g_HostnameVerifier, "verify", "(Ljava/lang/String;Ljavax/net/ssl/SSLSession;)Z"); + + g_HttpsURLConnection = GetClassGRef(env, "javax/net/ssl/HttpsURLConnection"); + g_HttpsURLConnectionGetDefaultHostnameVerifier = GetMethod(env, true, g_HttpsURLConnection, "getDefaultHostnameVerifier", "()Ljavax/net/ssl/HostnameVerifier;"); + + g_KeyManagerFactory = GetClassGRef(env, "javax/net/ssl/KeyManagerFactory"); + g_KeyManagerFactoryGetInstance = GetMethod(env, true, g_KeyManagerFactory, "getInstance", "(Ljava/lang/String;)Ljavax/net/ssl/KeyManagerFactory;"); + g_KeyManagerFactoryInit = GetMethod(env, false, g_KeyManagerFactory, "init", "(Ljava/security/KeyStore;[C)V"); + g_KeyManagerFactoryGetKeyManagers = GetMethod(env, false, g_KeyManagerFactory, "getKeyManagers", "()[Ljavax/net/ssl/KeyManager;"); + + g_SNIHostName = GetClassGRef(env, "javax/net/ssl/SNIHostName"); + g_SNIHostNameCtor = GetMethod(env, false, g_SNIHostName, "", "(Ljava/lang/String;)V"); + + g_SSLEngine = GetClassGRef(env, "javax/net/ssl/SSLEngine"); + g_SSLEngineGetApplicationProtocol = GetMethod(env, false, g_SSLEngine, "getApplicationProtocol", "()Ljava/lang/String;"); + g_SSLEngineSetUseClientMode = GetMethod(env, false, g_SSLEngine, "setUseClientMode", "(Z)V"); + g_SSLEngineGetSession = GetMethod(env, false, g_SSLEngine, "getSession", "()Ljavax/net/ssl/SSLSession;"); + g_SSLEngineBeginHandshake = GetMethod(env, false, g_SSLEngine, "beginHandshake", "()V"); + g_SSLEngineWrap = GetMethod(env, false, g_SSLEngine, "wrap", "(Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;)Ljavax/net/ssl/SSLEngineResult;"); + g_SSLEngineUnwrap = GetMethod(env, false, g_SSLEngine, "unwrap", "(Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;)Ljavax/net/ssl/SSLEngineResult;"); + g_SSLEngineGetHandshakeStatus = GetMethod(env, false, g_SSLEngine, "getHandshakeStatus", "()Ljavax/net/ssl/SSLEngineResult$HandshakeStatus;"); + g_SSLEngineCloseOutbound = GetMethod(env, false, g_SSLEngine, "closeOutbound", "()V"); + g_SSLEngineGetSupportedProtocols = GetMethod(env, false, g_SSLEngine, "getSupportedProtocols", "()[Ljava/lang/String;"); + g_SSLEngineSetEnabledProtocols = GetMethod(env, false, g_SSLEngine, "setEnabledProtocols", "([Ljava/lang/String;)V"); + g_SSLEngineSetSSLParameters = GetMethod(env, false, g_SSLEngine, "setSSLParameters", "(Ljavax/net/ssl/SSLParameters;)V"); + + g_ByteBuffer = GetClassGRef(env, "java/nio/ByteBuffer"); + g_ByteBufferAllocate = GetMethod(env, true, g_ByteBuffer, "allocate", "(I)Ljava/nio/ByteBuffer;"); + g_ByteBufferCompact = GetMethod(env, false, g_ByteBuffer, "compact", "()Ljava/nio/ByteBuffer;"); + g_ByteBufferFlip = GetMethod(env, false, g_ByteBuffer, "flip", "()Ljava/nio/Buffer;"); + g_ByteBufferGet = GetMethod(env, false, g_ByteBuffer, "get", "([B)Ljava/nio/ByteBuffer;"); + g_ByteBufferLimit = GetMethod(env, false, g_ByteBuffer, "limit", "()I"); + g_ByteBufferPosition = GetMethod(env, false, g_ByteBuffer, "position", "()I"); + g_ByteBufferPutBuffer = GetMethod(env, false, g_ByteBuffer, "put", "(Ljava/nio/ByteBuffer;)Ljava/nio/ByteBuffer;"); + g_ByteBufferPutByteArray = GetMethod(env, false, g_ByteBuffer, "put", "([B)Ljava/nio/ByteBuffer;"); + g_ByteBufferPutByteArrayWithLength = GetMethod(env, false, g_ByteBuffer, "put", "([BII)Ljava/nio/ByteBuffer;"); + g_ByteBufferRemaining = GetMethod(env, false, g_ByteBuffer, "remaining", "()I"); g_SSLContext = GetClassGRef(env, "javax/net/ssl/SSLContext"); + g_SSLContextGetDefault = GetMethod(env, true, g_SSLContext, "getDefault", "()Ljavax/net/ssl/SSLContext;"); g_SSLContextGetInstanceMethod = GetMethod(env, true, g_SSLContext, "getInstance", "(Ljava/lang/String;)Ljavax/net/ssl/SSLContext;"); g_SSLContextInitMethod = GetMethod(env, false, g_SSLContext, "init", "([Ljavax/net/ssl/KeyManager;[Ljavax/net/ssl/TrustManager;Ljava/security/SecureRandom;)V"); g_SSLContextCreateSSLEngineMethod = GetMethod(env, false, g_SSLContext, "createSSLEngine", "()Ljavax/net/ssl/SSLEngine;"); - g_SSLSession = GetClassGRef(env, "javax/net/ssl/SSLSession"); - g_SSLSessionGetApplicationBufferSizeMethod = GetMethod(env, false, g_SSLSession, "getApplicationBufferSize", "()I"); - g_SSLSessionGetPacketBufferSizeMethod = GetMethod(env, false, g_SSLSession, "getPacketBufferSize", "()I"); - - g_SSLEngineResult = GetClassGRef(env, "javax/net/ssl/SSLEngineResult"); - g_SSLEngineResultGetStatusMethod = GetMethod(env, false, g_SSLEngineResult, "getStatus", "()Ljavax/net/ssl/SSLEngineResult$Status;"); - g_SSLEngineResultGetHandshakeStatusMethod = GetMethod(env, false, g_SSLEngineResult, "getHandshakeStatus", "()Ljavax/net/ssl/SSLEngineResult$HandshakeStatus;"); + g_SSLSession = GetClassGRef(env, "javax/net/ssl/SSLSession"); + g_SSLSessionGetApplicationBufferSize = GetMethod(env, false, g_SSLSession, "getApplicationBufferSize", "()I"); + g_SSLSessionGetCipherSuite = GetMethod(env, false, g_SSLSession, "getCipherSuite", "()Ljava/lang/String;"); + g_SSLSessionGetPacketBufferSize = GetMethod(env, false, g_SSLSession, "getPacketBufferSize", "()I"); + g_SSLSessionGetPeerCertificates = GetMethod(env, false, g_SSLSession, "getPeerCertificates", "()[Ljava/security/cert/Certificate;"); + g_SSLSessionGetProtocol = GetMethod(env, false, g_SSLSession, "getProtocol", "()Ljava/lang/String;"); - g_TrustManager = GetClassGRef(env, "javax/net/ssl/TrustManager"); + g_SSLEngineResult = GetClassGRef(env, "javax/net/ssl/SSLEngineResult"); + g_SSLEngineResultGetStatus = GetMethod(env, false, g_SSLEngineResult, "getStatus", "()Ljavax/net/ssl/SSLEngineResult$Status;"); + g_SSLEngineResultGetHandshakeStatus = GetMethod(env, false, g_SSLEngineResult, "getHandshakeStatus", "()Ljavax/net/ssl/SSLEngineResult$HandshakeStatus;"); g_KeyAgreementClass = GetClassGRef(env, "javax/crypto/KeyAgreement"); g_KeyAgreementGetInstance = GetMethod(env, true, g_KeyAgreementClass, "getInstance", "(Ljava/lang/String;)Ljavax/crypto/KeyAgreement;"); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_jni.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_jni.h index 1414e740ad3af5..9d6f7199376d0d 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_jni.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_jni.h @@ -11,6 +11,7 @@ #define FAIL 0 #define SUCCESS 1 +#define INSUFFICIENT_BUFFER -1 extern JavaVM* gJvm; @@ -23,6 +24,10 @@ extern jmethodID g_ByteArrayInputStreamReset; extern jclass g_Enum; extern jmethodID g_EnumOrdinal; +// java/lang/String +extern jclass g_String; +extern jmethodID g_StringGetBytes; + // java/lang/Throwable extern jclass g_ThrowableClass; extern jmethodID g_ThrowableGetCause; @@ -82,8 +87,10 @@ extern jmethodID g_bitLengthMethod; extern jmethodID g_sigNumMethod; // javax/net/ssl/SSLParameters -extern jclass g_sslParamsClass; -extern jmethodID g_sslParamsGetProtocolsMethod; +extern jclass g_SSLParametersClass; +extern jmethodID g_SSLParametersCtor; +extern jmethodID g_SSLParametersGetProtocols; +extern jmethodID g_SSLParametersSetServerNames; // javax/net/ssl/SSLContext extern jclass g_sslCtxClass; @@ -204,6 +211,7 @@ extern jmethodID g_keyPairGenGenKeyPairMethod; // java/security/KeyStore extern jclass g_KeyStoreClass; +extern jmethodID g_KeyStoreGetDefaultType; extern jmethodID g_KeyStoreGetInstance; extern jmethodID g_KeyStoreAliases; extern jmethodID g_KeyStoreContainsAlias; @@ -331,6 +339,10 @@ extern jmethodID g_EllipticCurveGetB; extern jmethodID g_EllipticCurveGetField; extern jmethodID g_EllipticCurveGetSeed; +// java/security/spec/PKCS8EncodedKeySpec +extern jclass g_PKCS8EncodedKeySpec; +extern jmethodID g_PKCS8EncodedKeySpecCtor; + // java/security/spec/X509EncodedKeySpec extern jclass g_X509EncodedKeySpecClass; extern jmethodID g_X509EncodedKeySpecCtor; @@ -351,6 +363,11 @@ extern jclass g_CollectionClass; extern jmethodID g_CollectionIterator; extern jmethodID g_CollectionSize; +// java/util/ArrayList +extern jclass g_ArrayList; +extern jmethodID g_ArrayListCtor; +extern jmethodID g_ArrayListAdd; + // java/util/Date extern jclass g_DateClass; extern jmethodID g_DateCtor; @@ -374,49 +391,71 @@ extern jmethodID g_IteratorNext; extern jclass g_ListClass; extern jmethodID g_ListGet; +// javax/net/ssl/HostnameVerifier +extern jclass g_HostnameVerifier; +extern jmethodID g_HostnameVerifierVerify; + +// javax/net/ssl/HttpsURLConnection +extern jclass g_HttpsURLConnection; +extern jmethodID g_HttpsURLConnectionGetDefaultHostnameVerifier; + +// javax/net/ssl/KeyManagerFactory +extern jclass g_KeyManagerFactory; +extern jmethodID g_KeyManagerFactoryGetInstance; +extern jmethodID g_KeyManagerFactoryInit; +extern jmethodID g_KeyManagerFactoryGetKeyManagers; + +// javax/net/ssl/SNIHostName +extern jclass g_SNIHostName; +extern jmethodID g_SNIHostNameCtor; + // javax/net/ssl/SSLEngine extern jclass g_SSLEngine; -extern jmethodID g_SSLEngineSetUseClientModeMethod; -extern jmethodID g_SSLEngineGetSessionMethod; -extern jmethodID g_SSLEngineBeginHandshakeMethod; -extern jmethodID g_SSLEngineWrapMethod; -extern jmethodID g_SSLEngineUnwrapMethod; -extern jmethodID g_SSLEngineCloseInboundMethod; -extern jmethodID g_SSLEngineCloseOutboundMethod; -extern jmethodID g_SSLEngineGetHandshakeStatusMethod; +extern jmethodID g_SSLEngineGetApplicationProtocol; +extern jmethodID g_SSLEngineSetUseClientMode; +extern jmethodID g_SSLEngineGetSession; +extern jmethodID g_SSLEngineBeginHandshake; +extern jmethodID g_SSLEngineWrap; +extern jmethodID g_SSLEngineUnwrap; +extern jmethodID g_SSLEngineCloseOutbound; +extern jmethodID g_SSLEngineGetHandshakeStatus; +extern jmethodID g_SSLEngineGetSupportedProtocols; +extern jmethodID g_SSLEngineSetEnabledProtocols; +extern jmethodID g_SSLEngineSetSSLParameters; // java/nio/ByteBuffer extern jclass g_ByteBuffer; -extern jmethodID g_ByteBufferAllocateMethod; -extern jmethodID g_ByteBufferPutMethod; -extern jmethodID g_ByteBufferPut2Method; -extern jmethodID g_ByteBufferPut3Method; -extern jmethodID g_ByteBufferFlipMethod; -extern jmethodID g_ByteBufferGetMethod; -extern jmethodID g_ByteBufferLimitMethod; -extern jmethodID g_ByteBufferRemainingMethod; -extern jmethodID g_ByteBufferPutBufferMethod; -extern jmethodID g_ByteBufferCompactMethod; -extern jmethodID g_ByteBufferPositionMethod; +extern jmethodID g_ByteBufferAllocate; +extern jmethodID g_ByteBufferCompact; +extern jmethodID g_ByteBufferFlip; +extern jmethodID g_ByteBufferGet; +extern jmethodID g_ByteBufferLimit; +extern jmethodID g_ByteBufferPosition; +extern jmethodID g_ByteBufferPutBuffer; +extern jmethodID g_ByteBufferPutByteArray; +extern jmethodID g_ByteBufferPutByteArrayWithLength; +extern jmethodID g_ByteBufferRemaining; // javax/net/ssl/SSLContext extern jclass g_SSLContext; +extern jmethodID g_SSLContextGetDefault; extern jmethodID g_SSLContextGetInstanceMethod; extern jmethodID g_SSLContextInitMethod; extern jmethodID g_SSLContextCreateSSLEngineMethod; +extern jmethodID g_SSLContextCreateSSLEngineWithPeer; // javax/net/ssl/SSLSession extern jclass g_SSLSession; -extern jmethodID g_SSLSessionGetApplicationBufferSizeMethod; -extern jmethodID g_SSLSessionGetPacketBufferSizeMethod; +extern jmethodID g_SSLSessionGetApplicationBufferSize; +extern jmethodID g_SSLSessionGetCipherSuite; +extern jmethodID g_SSLSessionGetPacketBufferSize; +extern jmethodID g_SSLSessionGetPeerCertificates; +extern jmethodID g_SSLSessionGetProtocol; // javax/net/ssl/SSLEngineResult extern jclass g_SSLEngineResult; -extern jmethodID g_SSLEngineResultGetStatusMethod; -extern jmethodID g_SSLEngineResultGetHandshakeStatusMethod; - -// javax/net/ssl/TrustManager -extern jclass g_TrustManager; +extern jmethodID g_SSLEngineResultGetStatus; +extern jmethodID g_SSLEngineResultGetHandshakeStatus; // javax/crypto/KeyAgreement extern jclass g_KeyAgreementClass; @@ -425,13 +464,18 @@ extern jmethodID g_KeyAgreementInit; extern jmethodID g_KeyAgreementDoPhase; extern jmethodID g_KeyAgreementGenerateSecret; -// JNI helpers +// Logging helpers #define LOG_DEBUG(fmt, ...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "DOTNET", "%s: " fmt, __FUNCTION__, ## __VA_ARGS__)) #define LOG_INFO(fmt, ...) ((void)__android_log_print(ANDROID_LOG_INFO, "DOTNET", "%s: " fmt, __FUNCTION__, ## __VA_ARGS__)) #define LOG_ERROR(fmt, ...) ((void)__android_log_print(ANDROID_LOG_ERROR, "DOTNET", "%s: " fmt, __FUNCTION__, ## __VA_ARGS__)) + +// JNI helpers - assume there is a JNIEnv* variable named env #define JSTRING(str) ((jstring)(*env)->NewStringUTF(env, str)) #define ON_EXCEPTION_PRINT_AND_GOTO(label) if (CheckJNIExceptions(env)) goto label +// Explicitly ignore jobject return value +#define IGNORE_RETURN(retval) (*env)->DeleteLocalRef(env, retval) + #define INIT_LOCALS(name, ...) \ enum { __VA_ARGS__, count_##name }; \ jobject name[count_##name] = { 0 } \ @@ -470,9 +514,6 @@ bool TryClearJNIExceptions(JNIEnv* env); // Get any pending JNI exception. Returns true if there was an exception, false otherwise. bool TryGetJNIException(JNIEnv* env, jthrowable *ex, bool printException); -// Assert on any JNI exceptions. Prints the exception before asserting. -void AssertOnJNIExceptions(JNIEnv* env); - jmethodID GetMethod(JNIEnv *env, bool isStatic, jclass klass, const char* name, const char* sig); jmethodID GetOptionalMethod(JNIEnv *env, bool isStatic, jclass klass, const char* name, const char* sig); jfieldID GetField(JNIEnv *env, bool isStatic, jclass klass, const char* name, const char* sig); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_ssl.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_ssl.c index fb7eac36c0b6f6..e512c77ae66f6b 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_ssl.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_ssl.c @@ -3,34 +3,51 @@ #include "pal_ssl.h" -int32_t CryptoNative_OpenSslGetProtocolSupport(SslProtocols protocol) +PAL_SslProtocol AndroidCryptoNative_SSLGetSupportedProtocols(void) { JNIEnv* env = GetJNIEnv(); - jobject sslCtxObj = (*env)->CallStaticObjectMethod(env, g_sslCtxClass, g_sslCtxGetDefaultMethod); - jobject sslParametersObj = (*env)->CallObjectMethod(env, sslCtxObj, g_sslCtxGetDefaultSslParamsMethod); - jobjectArray protocols = (jobjectArray)(*env)->CallObjectMethod(env, sslParametersObj, g_sslParamsGetProtocolsMethod); + PAL_SslProtocol supported = 0; + INIT_LOCALS(loc, context, params, protocols); - int protocolsCount = (*env)->GetArrayLength(env, protocols); - int supported = 0; - for (int i = 0; i < protocolsCount; i++) + // SSLContext context = SSLContext.getDefault(); + // SSLParameters params = context.getDefaultSSLParameters(); + // String[] protocols = params.getProtocols(); + loc[context] = (*env)->CallStaticObjectMethod(env, g_sslCtxClass, g_sslCtxGetDefaultMethod); + loc[params] = (*env)->CallObjectMethod(env, loc[context], g_sslCtxGetDefaultSslParamsMethod); + loc[protocols] = (*env)->CallObjectMethod(env, loc[params], g_SSLParametersGetProtocols); + + const char tlsv1[] = "TLSv1"; + size_t tlsv1Len = (sizeof(tlsv1) / sizeof(*tlsv1)) - 1; + + jsize count = (*env)->GetArrayLength(env, loc[protocols]); + for (int32_t i = 0; i < count; i++) { - jstring protocolStr = (jstring) ((*env)->GetObjectArrayElement(env, protocols, i)); - const char* protocolStrPtr = (*env)->GetStringUTFChars(env, protocolStr, NULL); - if ((!strcmp(protocolStrPtr, "TLSv1") && protocol == PAL_SSL_TLS) || - (!strcmp(protocolStrPtr, "TLSv1.1") && protocol == PAL_SSL_TLS11) || - (!strcmp(protocolStrPtr, "TLSv1.2") && protocol == PAL_SSL_TLS12) || - (!strcmp(protocolStrPtr, "TLSv1.3") && protocol == PAL_SSL_TLS13)) + jstring protocol = (*env)->GetObjectArrayElement(env, loc[protocols], i); + const char* protocolStr = (*env)->GetStringUTFChars(env, protocol, NULL); + if (strncmp(protocolStr, tlsv1, tlsv1Len) == 0) { - supported = 1; - (*env)->ReleaseStringUTFChars(env, protocolStr, protocolStrPtr); - (*env)->DeleteLocalRef(env, protocolStr); - break; + if (strlen(protocolStr) == tlsv1Len) + { + supported |= PAL_SslProtocol_Tls10; + } + else if (strcmp(protocolStr + tlsv1Len, ".1") == 0) + { + supported |= PAL_SslProtocol_Tls11; + } + else if (strcmp(protocolStr + tlsv1Len, ".2") == 0) + { + supported |= PAL_SslProtocol_Tls12; + } + else if (strcmp(protocolStr + tlsv1Len, ".3") == 0) + { + supported |= PAL_SslProtocol_Tls13; + } } - (*env)->ReleaseStringUTFChars(env, protocolStr, protocolStrPtr); - (*env)->DeleteLocalRef(env, protocolStr); + + (*env)->ReleaseStringUTFChars(env, protocol, protocolStr); + (*env)->DeleteLocalRef(env, protocol); } - (*env)->DeleteLocalRef(env, sslCtxObj); - (*env)->DeleteLocalRef(env, sslParametersObj); - (*env)->DeleteLocalRef(env, protocols); + + RELEASE_LOCALS(loc, env); return supported; } diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_ssl.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_ssl.h index 04227972f8175c..d4f1dcd23bd8c8 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_ssl.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_ssl.h @@ -4,16 +4,9 @@ #pragma once #include "pal_jni.h" +#include -typedef enum -{ - PAL_SSL_NONE = 0, - PAL_SSL_SSL2 = 12, - PAL_SSL_SSL3 = 48, - PAL_SSL_TLS = 192, - PAL_SSL_TLS11 = 768, - PAL_SSL_TLS12 = 3072, - PAL_SSL_TLS13 = 12288, -} SslProtocols; - -PALEXPORT int32_t CryptoNative_OpenSslGetProtocolSupport(SslProtocols protocol); +/* +Get the supported protocols +*/ +PALEXPORT PAL_SslProtocol AndroidCryptoNative_SSLGetSupportedProtocols(void); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_sslstream.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_sslstream.c index b8ac3849ad6bc7..be336f291b05b5 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_sslstream.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_sslstream.c @@ -3,97 +3,99 @@ #include "pal_sslstream.h" -void checkHandshakeStatus(JNIEnv* env, SSLStream* sslStream, int handshakeStatus); - -static int getHandshakeStatus(JNIEnv* env, SSLStream* sslStream, jobject engineResult) +// javax/net/ssl/SSLEngineResult$HandshakeStatus +enum { - AssertOnJNIExceptions(env); - int status = -1; - if (engineResult) - status = GetEnumAsInt(env, (*env)->CallObjectMethod(env, engineResult, g_SSLEngineResultGetHandshakeStatusMethod)); - else - status = GetEnumAsInt(env, (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetHandshakeStatusMethod)); - AssertOnJNIExceptions(env); - return status; -} + HANDSHAKE_STATUS__NOT_HANDSHAKING = 0, + HANDSHAKE_STATUS__FINISHED = 1, + HANDSHAKE_STATUS__NEED_TASK = 2, + HANDSHAKE_STATUS__NEED_WRAP = 3, + HANDSHAKE_STATUS__NEED_UNWRAP = 4, +}; + +// javax/net/ssl/SSLEngineResult$Status +enum +{ + STATUS__BUFFER_UNDERFLOW = 0, + STATUS__BUFFER_OVERFLOW = 1, + STATUS__OK = 2, + STATUS__CLOSED = 3, +}; -static void close(JNIEnv* env, SSLStream* sslStream) { - /* - sslEngine.closeOutbound(); - checkHandshakeStatus(); - */ +static uint16_t* AllocateString(JNIEnv* env, jstring source); + +static PAL_SSLStreamStatus DoHandshake(JNIEnv* env, SSLStream* sslStream); +static PAL_SSLStreamStatus DoWrap(JNIEnv* env, SSLStream* sslStream, int* handshakeStatus); +static PAL_SSLStreamStatus DoUnwrap(JNIEnv* env, SSLStream* sslStream, int* handshakeStatus); - AssertOnJNIExceptions(env); - (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineCloseOutboundMethod); - checkHandshakeStatus(env, sslStream, getHandshakeStatus(env, sslStream, NULL)); +static bool IsHandshaking(int handshakeStatus) +{ + return handshakeStatus != HANDSHAKE_STATUS__NOT_HANDSHAKING && handshakeStatus != HANDSHAKE_STATUS__FINISHED; } -static void handleEndOfStream(JNIEnv* env, SSLStream* sslStream) { - /* - sslEngine.closeInbound(); - close(); - */ +static PAL_SSLStreamStatus Close(JNIEnv* env, SSLStream* sslStream) +{ + // sslEngine.closeOutbound(); + (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineCloseOutbound); - AssertOnJNIExceptions(env); - (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineCloseInboundMethod); - close(env, sslStream); - AssertOnJNIExceptions(env); + // Call wrap to clear any remaining data + int unused; + return DoWrap(env, sslStream, &unused); } -static void flush(JNIEnv* env, SSLStream* sslStream) +static PAL_SSLStreamStatus Flush(JNIEnv* env, SSLStream* sslStream) { /* netOutBuffer.flip(); byte[] data = new byte[netOutBuffer.limit()]; netOutBuffer.get(data); - WriteToOutputStream(data, 0, data.length); + streamWriter(data, 0, data.length); netOutBuffer.compact(); */ - AssertOnJNIExceptions(env); + PAL_SSLStreamStatus ret = SSLStreamStatus_Error; - // DeleteLocalRef because we don't need the return value (Buffer) - (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->netOutBuffer, g_ByteBufferFlipMethod)); - int bufferLimit = (*env)->CallIntMethod(env, sslStream->netOutBuffer, g_ByteBufferLimitMethod); + IGNORE_RETURN((*env)->CallObjectMethod(env, sslStream->netOutBuffer, g_ByteBufferFlip)); + int32_t bufferLimit = (*env)->CallIntMethod(env, sslStream->netOutBuffer, g_ByteBufferLimit); jbyteArray data = (*env)->NewByteArray(env, bufferLimit); - // DeleteLocalRef because we don't need the return value (Buffer) - (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->netOutBuffer, g_ByteBufferGetMethod, data)); + IGNORE_RETURN((*env)->CallObjectMethod(env, sslStream->netOutBuffer, g_ByteBufferGet, data)); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); uint8_t* dataPtr = (uint8_t*)malloc((size_t)bufferLimit); - (*env)->GetByteArrayRegion(env, data, 0, bufferLimit, (jbyte*) dataPtr); - sslStream->streamWriter(dataPtr, 0, (uint32_t)bufferLimit); + (*env)->GetByteArrayRegion(env, data, 0, bufferLimit, (jbyte*)dataPtr); + sslStream->streamWriter(dataPtr, bufferLimit); free(dataPtr); - // DeleteLocalRef because we don't need the return value (Buffer) - (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->netOutBuffer, g_ByteBufferCompactMethod)); - AssertOnJNIExceptions(env); + + IGNORE_RETURN((*env)->CallObjectMethod(env, sslStream->netOutBuffer, g_ByteBufferCompact)); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + ret = SSLStreamStatus_OK; + +cleanup: + (*env)->DeleteLocalRef(env, data); + return ret; } -static jobject ensureRemaining(JNIEnv* env, SSLStream* sslStream, jobject oldBuffer, int newRemaining) +static jobject ExpandBuffer(JNIEnv* env, jobject oldBuffer, int32_t newCapacity) { - /* - if (oldBuffer.remaining() < newRemaining) { - oldBuffer.flip(); - final ByteBuffer newBuffer = ByteBuffer.allocate(oldBuffer.remaining() + newRemaining); - newBuffer.put(oldBuffer); - return newBuffer; - } else { - return oldBuffer; - } - */ - - AssertOnJNIExceptions(env); + // oldBuffer.flip(); + // ByteBuffer newBuffer = ByteBuffer.allocate(newCapacity); + // newBuffer.put(oldBuffer); + IGNORE_RETURN((*env)->CallObjectMethod(env, oldBuffer, g_ByteBufferFlip)); + jobject newBuffer = + ToGRef(env, (*env)->CallStaticObjectMethod(env, g_ByteBuffer, g_ByteBufferAllocate, newCapacity)); + IGNORE_RETURN((*env)->CallObjectMethod(env, newBuffer, g_ByteBufferPutBuffer, oldBuffer)); + ReleaseGRef(env, oldBuffer); + return newBuffer; +} - int oldRemaining = (*env)->CallIntMethod(env, oldBuffer, g_ByteBufferRemainingMethod); +static jobject EnsureRemaining(JNIEnv* env, jobject oldBuffer, int32_t newRemaining) +{ + int32_t oldRemaining = (*env)->CallIntMethod(env, oldBuffer, g_ByteBufferRemaining); if (oldRemaining < newRemaining) { - // DeleteLocalRef because we don't need the return value (Buffer) - (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, oldBuffer, g_ByteBufferFlipMethod)); - jobject newBuffer = ToGRef(env, (*env)->CallStaticObjectMethod(env, g_ByteBuffer, g_ByteBufferAllocateMethod, oldRemaining + newRemaining)); - // DeleteLocalRef because we don't need the return value (Buffer) - (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, newBuffer, g_ByteBufferPutBufferMethod, oldBuffer)); - ReleaseGRef(env, oldBuffer); - return newBuffer; + return ExpandBuffer(env, oldBuffer, oldRemaining + newRemaining); } else { @@ -101,314 +103,751 @@ static jobject ensureRemaining(JNIEnv* env, SSLStream* sslStream, jobject oldBuf } } -static void doWrap(JNIEnv* env, SSLStream* sslStream) +static PAL_SSLStreamStatus DoWrap(JNIEnv* env, SSLStream* sslStream, int* handshakeStatus) { - /* - appOutBuffer.flip(); - final SSLEngineResult result; - try { - result = sslEngine.wrap(appOutBuffer, netOutBuffer); - } catch (SSLException e) { - return; - } - appOutBuffer.compact(); - - final SSLEngineResult.Status status = result.getStatus(); - switch (status) { - case OK: - flush(); - checkHandshakeStatus(result.getHandshakeStatus()); - if (appOutBuffer.position() > 0) doWrap(); - break; - case CLOSED: - flush(); - checkHandshakeStatus(result.getHandshakeStatus()); - close(); - break; - case BUFFER_OVERFLOW: - netOutBuffer = ensureRemaining(netOutBuffer, sslEngine.getSession().getPacketBufferSize()); - doWrap(); - break; - } - */ - - AssertOnJNIExceptions(env); + // appOutBuffer.flip(); + // SSLEngineResult result = sslEngine.wrap(appOutBuffer, netOutBuffer); + IGNORE_RETURN((*env)->CallObjectMethod(env, sslStream->appOutBuffer, g_ByteBufferFlip)); + jobject result = (*env)->CallObjectMethod( + env, sslStream->sslEngine, g_SSLEngineWrap, sslStream->appOutBuffer, sslStream->netOutBuffer); + if (CheckJNIExceptions(env)) + return SSLStreamStatus_Error; + + // appOutBuffer.compact(); + IGNORE_RETURN((*env)->CallObjectMethod(env, sslStream->appOutBuffer, g_ByteBufferCompact)); + + // handshakeStatus = result.getHandshakeStatus(); + // SSLEngineResult.Status status = result.getStatus(); + *handshakeStatus = GetEnumAsInt(env, (*env)->CallObjectMethod(env, result, g_SSLEngineResultGetHandshakeStatus)); + int status = GetEnumAsInt(env, (*env)->CallObjectMethod(env, result, g_SSLEngineResultGetStatus)); + (*env)->DeleteLocalRef(env, result); - (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->appOutBuffer, g_ByteBufferFlipMethod)); - jobject sslEngineResult = (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineWrapMethod, sslStream->appOutBuffer, sslStream->netOutBuffer); - (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->appOutBuffer, g_ByteBufferCompactMethod)); - - int status = GetEnumAsInt(env, (*env)->CallObjectMethod(env, sslEngineResult, g_SSLEngineResultGetStatusMethod)); switch (status) { case STATUS__OK: - flush(env, sslStream); - checkHandshakeStatus(env, sslStream, getHandshakeStatus(env, sslStream, sslEngineResult)); - if ((*env)->CallIntMethod(env, sslStream->appOutBuffer, g_ByteBufferPositionMethod) > 0) - doWrap(env, sslStream); - break; + { + return Flush(env, sslStream); + } case STATUS__CLOSED: - flush(env, sslStream); - checkHandshakeStatus(env, sslStream, getHandshakeStatus(env, sslStream, sslEngineResult)); - close(env, sslStream); - break; - case STATUS__BUFFER_OVERFLOW: - sslStream->netOutBuffer = ensureRemaining(env, sslStream, sslStream->netOutBuffer, (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetPacketBufferSizeMethod)); - doWrap(env, sslStream); - break; - } -} - -static void doUnwrap(JNIEnv* env, SSLStream* sslStream) -{ - /* - if (netInBuffer.position() == 0) { - byte[] tmp = new byte[netInBuffer.limit()]; - - int count = ReadFromInputStream(tmp, 0, tmp.length); - if (count == -1) { - handleEndOfStream(); - return; - } - netInBuffer.put(tmp, 0, count); + (void)Flush(env, sslStream); + (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineCloseOutbound); + return SSLStreamStatus_Closed; } - - netInBuffer.flip(); - final SSLEngineResult result; - try { - result = sslEngine.unwrap(netInBuffer, appInBuffer); - } catch (SSLException e) { - return; + case STATUS__BUFFER_OVERFLOW: + { + // Expand buffer + // int newCapacity = sslSession.getPacketBufferSize() + netOutBuffer.remaining(); + int32_t newCapacity = (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetPacketBufferSize) + + (*env)->CallIntMethod(env, sslStream->netOutBuffer, g_ByteBufferRemaining); + sslStream->netOutBuffer = ExpandBuffer(env, sslStream->netOutBuffer, newCapacity); + return SSLStreamStatus_OK; } - netInBuffer.compact(); - final SSLEngineResult.Status status = result.getStatus(); - switch (status) { - case OK: - checkHandshakeStatus(result.getHandshakeStatus()); - break; - case CLOSED: - checkHandshakeStatus(result.getHandshakeStatus()); - close(); - break; - case BUFFER_UNDERFLOW: - netInBuffer = ensureRemaining(netInBuffer, sslEngine.getSession().getPacketBufferSize()); - doUnwrap(); - break; - case BUFFER_OVERFLOW: - appInBuffer = ensureRemaining(appInBuffer, sslEngine.getSession().getApplicationBufferSize()); - doUnwrap(); - break; + default: + { + LOG_ERROR("Unknown SSLEngineResult status: %d", status); + return SSLStreamStatus_Error; } - */ - - AssertOnJNIExceptions(env); + } +} - if ((*env)->CallIntMethod(env, sslStream->netInBuffer, g_ByteBufferPositionMethod) == 0) +static PAL_SSLStreamStatus DoUnwrap(JNIEnv* env, SSLStream* sslStream, int* handshakeStatus) +{ + // if (netInBuffer.position() == 0) + // { + // byte[] tmp = new byte[netInBuffer.limit()]; + // int count = streamReader(tmp, 0, tmp.length); + // netInBuffer.put(tmp, 0, count); + // } + if ((*env)->CallIntMethod(env, sslStream->netInBuffer, g_ByteBufferPosition) == 0) { - int netInBufferLimit = (*env)->CallIntMethod(env, sslStream->netInBuffer, g_ByteBufferLimitMethod); + int netInBufferLimit = (*env)->CallIntMethod(env, sslStream->netInBuffer, g_ByteBufferLimit); jbyteArray tmp = (*env)->NewByteArray(env, netInBufferLimit); uint8_t* tmpNative = (uint8_t*)malloc((size_t)netInBufferLimit); - int count = sslStream->streamReader(tmpNative, 0, (uint32_t)netInBufferLimit); - if (count == -1) + int count = netInBufferLimit; + PAL_SSLStreamStatus status = sslStream->streamReader(tmpNative, &count); + if (status != SSLStreamStatus_OK) { - handleEndOfStream(env, sslStream); - return; + (*env)->DeleteLocalRef(env, tmp); + return status; } + (*env)->SetByteArrayRegion(env, tmp, 0, count, (jbyte*)(tmpNative)); - (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->netInBuffer, g_ByteBufferPut3Method, tmp, 0, count)); + IGNORE_RETURN( + (*env)->CallObjectMethod(env, sslStream->netInBuffer, g_ByteBufferPutByteArrayWithLength, tmp, 0, count)); free(tmpNative); (*env)->DeleteLocalRef(env, tmp); } - (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->netInBuffer, g_ByteBufferFlipMethod)); - jobject sslEngineResult = (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineUnwrapMethod, sslStream->netInBuffer, sslStream->appInBuffer); - (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->netInBuffer, g_ByteBufferCompactMethod)); - - int status = GetEnumAsInt(env, (*env)->CallObjectMethod(env, sslEngineResult, g_SSLEngineResultGetStatusMethod)); + // netInBuffer.flip(); + // SSLEngineResult result = sslEngine.unwrap(netInBuffer, appInBuffer); + IGNORE_RETURN((*env)->CallObjectMethod(env, sslStream->netInBuffer, g_ByteBufferFlip)); + jobject result = (*env)->CallObjectMethod( + env, sslStream->sslEngine, g_SSLEngineUnwrap, sslStream->netInBuffer, sslStream->appInBuffer); + if (CheckJNIExceptions(env)) + return SSLStreamStatus_Error; + + // netInBuffer.compact(); + IGNORE_RETURN((*env)->CallObjectMethod(env, sslStream->netInBuffer, g_ByteBufferCompact)); + + // handshakeStatus = result.getHandshakeStatus(); + // SSLEngineResult.Status status = result.getStatus(); + *handshakeStatus = GetEnumAsInt(env, (*env)->CallObjectMethod(env, result, g_SSLEngineResultGetHandshakeStatus)); + int status = GetEnumAsInt(env, (*env)->CallObjectMethod(env, result, g_SSLEngineResultGetStatus)); + (*env)->DeleteLocalRef(env, result); switch (status) { case STATUS__OK: - checkHandshakeStatus(env, sslStream, getHandshakeStatus(env, sslStream, sslEngineResult)); - break; + { + return SSLStreamStatus_OK; + } case STATUS__CLOSED: - checkHandshakeStatus(env, sslStream, getHandshakeStatus(env, sslStream, sslEngineResult)); - close(env, sslStream); - break; + { + return Close(env, sslStream); + } case STATUS__BUFFER_UNDERFLOW: - sslStream->netInBuffer = ensureRemaining(env, sslStream, sslStream->netInBuffer, (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetPacketBufferSizeMethod)); - doUnwrap(env, sslStream); - break; + { + // Expand buffer + // int newRemaining = sslSession.getPacketBufferSize(); + int32_t newRemaining = (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetPacketBufferSize); + sslStream->netInBuffer = EnsureRemaining(env, sslStream->netInBuffer, newRemaining); + return SSLStreamStatus_OK; + } case STATUS__BUFFER_OVERFLOW: - sslStream->appInBuffer = ensureRemaining(env, sslStream, sslStream->appInBuffer, (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetApplicationBufferSizeMethod)); - doUnwrap(env, sslStream); - break; + { + // Expand buffer + // int newCapacity = sslSession.getApplicationBufferSize() + appInBuffer.remaining(); + int32_t newCapacity = + (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetApplicationBufferSize) + + (*env)->CallIntMethod(env, sslStream->appInBuffer, g_ByteBufferRemaining); + sslStream->appInBuffer = ExpandBuffer(env, sslStream->appInBuffer, newCapacity); + return SSLStreamStatus_OK; + } + default: + { + LOG_ERROR("Unknown SSLEngineResult status: %d", status); + return SSLStreamStatus_Error; + } } } -void checkHandshakeStatus(JNIEnv* env, SSLStream* sslStream, int handshakeStatus) +static PAL_SSLStreamStatus DoHandshake(JNIEnv* env, SSLStream* sslStream) { - /* - switch (handshakeStatus) { - case NEED_WRAP: - doWrap(); + assert(env != NULL); + assert(sslStream != NULL); + + PAL_SSLStreamStatus status = SSLStreamStatus_OK; + int handshakeStatus = + GetEnumAsInt(env, (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetHandshakeStatus)); + while (IsHandshaking(handshakeStatus) && status == SSLStreamStatus_OK) + { + switch (handshakeStatus) + { + case HANDSHAKE_STATUS__NEED_WRAP: + status = DoWrap(env, sslStream, &handshakeStatus); break; - case NEED_UNWRAP: - doUnwrap(); + case HANDSHAKE_STATUS__NEED_UNWRAP: + status = DoUnwrap(env, sslStream, &handshakeStatus); break; - case NEED_TASK: - Runnable task; - while ((task = sslEngine.getDelegatedTask()) != null) task.run(); - checkHandshakeStatus(); + case HANDSHAKE_STATUS__NOT_HANDSHAKING: + case HANDSHAKE_STATUS__FINISHED: + status = SSLStreamStatus_OK; break; + case HANDSHAKE_STATUS__NEED_TASK: + assert(0 && "unexpected NEED_TASK handshake status"); } - */ + } + + return status; +} + +static void FreeSSLStream(JNIEnv* env, SSLStream* sslStream) +{ + assert(sslStream != NULL); + ReleaseGRef(env, sslStream->sslContext); + ReleaseGRef(env, sslStream->sslEngine); + ReleaseGRef(env, sslStream->sslSession); + ReleaseGRef(env, sslStream->appOutBuffer); + ReleaseGRef(env, sslStream->netOutBuffer); + ReleaseGRef(env, sslStream->netInBuffer); + ReleaseGRef(env, sslStream->appInBuffer); + free(sslStream); +} + +SSLStream* AndroidCryptoNative_SSLStreamCreate(void) +{ + JNIEnv* env = GetJNIEnv(); + + // TODO: [AndroidCrypto] If we have certificates, get an SSLContext instance with the highest available + // protocol - TLSv1.2 (API level 16+) or TLSv1.3 (API level 29+), use KeyManagerFactory to create key + // managers that will return the certificates, and initialize the SSLContext with the key managers. + + // SSLContext sslContext = SSLContext.getDefault(); + jobject sslContext = (*env)->CallStaticObjectMethod(env, g_SSLContext, g_SSLContextGetDefault); + if (CheckJNIExceptions(env)) + return NULL; + + SSLStream* sslStream = malloc(sizeof(SSLStream)); + memset(sslStream, 0, sizeof(SSLStream)); + sslStream->sslContext = ToGRef(env, sslContext); + return sslStream; +} - AssertOnJNIExceptions(env); - switch (handshakeStatus) +static int32_t AddCertChainToStore(JNIEnv* env, + jobject store, + uint8_t* pkcs8PrivateKey, + int32_t pkcs8PrivateKeyLen, + PAL_KeyAlgorithm algorithm, + jobject* /*X509Certificate[]*/ certs, + int32_t certsLen) +{ + int32_t ret = FAIL; + INIT_LOCALS(loc, keyBytes, keySpec, algorithmName, keyFactory, privateKey, certArray, alias); + + // byte[] keyBytes = new byte[] { }; + // PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); + loc[keyBytes] = (*env)->NewByteArray(env, pkcs8PrivateKeyLen); + (*env)->SetByteArrayRegion(env, loc[keyBytes], 0, pkcs8PrivateKeyLen, (jbyte*)pkcs8PrivateKey); + loc[keySpec] = (*env)->NewObject(env, g_PKCS8EncodedKeySpec, g_PKCS8EncodedKeySpecCtor, loc[keyBytes]); + + switch (algorithm) { - case HANDSHAKE_STATUS__NEED_WRAP: - doWrap(env, sslStream); + case PAL_DSA: + loc[algorithmName] = JSTRING("DSA"); + break; + case PAL_EC: + loc[algorithmName] = JSTRING("EC"); break; - case HANDSHAKE_STATUS__NEED_UNWRAP: - doUnwrap(env, sslStream); + case PAL_RSA: + loc[algorithmName] = JSTRING("RSA"); break; - case HANDSHAKE_STATUS__NEED_TASK: - assert(0 && "unexpected NEED_TASK handshake status"); + default: + LOG_ERROR("Unknown key algorithm: %d", algorithm); + goto cleanup; + } + + // KeyFactory keyFactory = KeyFactory.getInstance(algorithmName); + // PrivateKey privateKey = keyFactory.generatePrivate(spec); + loc[keyFactory] = + (*env)->CallStaticObjectMethod(env, g_KeyFactoryClass, g_KeyFactoryGetInstanceMethod, loc[algorithmName]); + loc[privateKey] = (*env)->CallObjectMethod(env, loc[keyFactory], g_KeyFactoryGenPrivateMethod, loc[keySpec]); + + // X509Certificate[] certArray = new X509Certificate[certsLen]; + loc[certArray] = (*env)->NewObjectArray(env, certsLen, g_X509CertClass, NULL); + for (int32_t i = 0; i < certsLen; ++i) + { + (*env)->SetObjectArrayElement(env, loc[certArray], i, certs[i]); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); } + + // store.setKeyEntry("SSLCertificateContext", privateKey, null, certArray); + loc[alias] = JSTRING("SSLCertificateContext"); + (*env)->CallVoidMethod(env, store, g_KeyStoreSetKeyEntry, loc[alias], loc[privateKey], NULL, loc[certArray]); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + ret = SUCCESS; + +cleanup: + RELEASE_LOCALS(loc, env); + return ret; } -SSLStream* AndroidCryptoNative_SSLStreamCreateAndStartHandshake( - STREAM_READER streamReader, - STREAM_WRITER streamWriter, - int tlsVersion, - int appOutBufferSize, - int appInBufferSize) +SSLStream* AndroidCryptoNative_SSLStreamCreateWithCertificates(uint8_t* pkcs8PrivateKey, + int32_t pkcs8PrivateKeyLen, + PAL_KeyAlgorithm algorithm, + jobject* /*X509Certificate[]*/ certs, + int32_t certsLen) { + SSLStream* sslStream = NULL; JNIEnv* env = GetJNIEnv(); - /* - SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); - sslContext.init(null, new TrustManage r[]{trustAllCerts}, null); - this.sslEngine = sslContext.createSSLEngine(); - this.sslEngine.setUseClientMode(true); - SSLSession sslSession = sslEngine.getSession(); - final int applicationBufferSize = sslSession.getApplicationBufferSize(); - final int packetBufferSize = sslSession.getPacketBufferSize(); - this.appOutBuffer = ByteBuffer.allocate(appOutBufferSize); - this.netOutBuffer = ByteBuffer.allocate(packetBufferSize); - this.netInBuffer = ByteBuffer.allocate(packetBufferSize); - this.appInBuffer = ByteBuffer.allocate(Math.max(applicationBufferSize, appInBufferSize)); - sslEngine.beginHandshake(); - */ - - SSLStream* sslStream = malloc(sizeof(SSLStream)); - jobject tlsVerStr = NULL; - if (tlsVersion == 11) - tlsVerStr = JSTRING("TLSv1.1"); - else if (tlsVersion == 12) - tlsVerStr = JSTRING("TLSv1.2"); - else if (tlsVersion == 13) - tlsVerStr = JSTRING("TLSv1.3"); - else - assert(0 && "unknown tlsVersion"); + INIT_LOCALS(loc, tls13, sslContext, ksType, keyStore, kmfType, kmf, keyManagers); - sslStream->sslContext = ToGRef(env, (*env)->CallStaticObjectMethod(env, g_SSLContext, g_SSLContextGetInstanceMethod, tlsVerStr)); + // SSLContext sslContext = SSLContext.getInstance("TLSv1.3"); + loc[tls13] = JSTRING("TLSv1.3"); + loc[sslContext] = (*env)->CallStaticObjectMethod(env, g_SSLContext, g_SSLContextGetInstanceMethod, loc[tls13]); + if (TryClearJNIExceptions(env)) + { + // TLSv1.3 is only supported on API level 29+ - fall back to TLSv1.2 (which is supported on API level 16+) + // sslContext = SSLContext.getInstance("TLSv1.2"); + jobject tls12 = JSTRING("TLSv1.2"); + loc[sslContext] = (*env)->CallStaticObjectMethod(env, g_SSLContext, g_SSLContextGetInstanceMethod, tls12); + ReleaseLRef(env, tls12); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + } - // TODO: set TrustManager[] argument to be able to intercept cert validation process (and callback to C#). - (*env)->CallVoidMethod(env, sslStream->sslContext, g_SSLContextInitMethod, NULL, NULL, NULL); - sslStream->sslEngine = ToGRef(env, (*env)->CallObjectMethod(env, sslStream->sslContext, g_SSLContextCreateSSLEngineMethod)); - sslStream->sslSession = ToGRef(env, (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetSessionMethod)); + // String ksType = KeyStore.getDefaultType(); + // KeyStore keyStore = KeyStore.getInstance(ksType); + // keyStore.load(null, null); + loc[ksType] = (*env)->CallStaticObjectMethod(env, g_KeyStoreClass, g_KeyStoreGetDefaultType); + loc[keyStore] = (*env)->CallStaticObjectMethod(env, g_KeyStoreClass, g_KeyStoreGetInstance, loc[ksType]); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + (*env)->CallVoidMethod(env, loc[keyStore], g_KeyStoreLoad, NULL, NULL); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + int32_t status = + AddCertChainToStore(env, loc[keyStore], pkcs8PrivateKey, pkcs8PrivateKeyLen, algorithm, certs, certsLen); + if (status != SUCCESS) + goto cleanup; + + // String kmfType = "PKIX"; + // KeyManagerFactory kmf = KeyManagerFactory.getInstance(kmfType); + loc[kmfType] = JSTRING("PKIX"); + loc[kmf] = (*env)->CallStaticObjectMethod(env, g_KeyManagerFactory, g_KeyManagerFactoryGetInstance, loc[kmfType]); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + // kmf.init(keyStore, null); + (*env)->CallVoidMethod(env, loc[kmf], g_KeyManagerFactoryInit, loc[keyStore], NULL); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + // KeyManager[] keyManagers = kmf.getKeyManagers(); + // sslContext.init(keyManagers, null, null); + loc[keyManagers] = (*env)->CallObjectMethod(env, loc[kmf], g_KeyManagerFactoryGetKeyManagers); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + (*env)->CallVoidMethod(env, loc[sslContext], g_SSLContextInitMethod, loc[keyManagers], NULL, NULL); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + sslStream = malloc(sizeof(SSLStream)); + memset(sslStream, 0, sizeof(SSLStream)); + sslStream->sslContext = ToGRef(env, loc[sslContext]); + loc[sslContext] = NULL; + +cleanup: + RELEASE_LOCALS(loc, env); + return sslStream; +} - int applicationBufferSize = (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetApplicationBufferSizeMethod); - int packetBufferSize = (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetPacketBufferSizeMethod); +int32_t AndroidCryptoNative_SSLStreamInitialize( + SSLStream* sslStream, bool isServer, STREAM_READER streamReader, STREAM_WRITER streamWriter, int32_t appBufferSize) +{ + assert(sslStream != NULL); + assert(sslStream->sslContext != NULL); + assert(sslStream->sslEngine == NULL); + assert(sslStream->sslSession == NULL); - (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineSetUseClientModeMethod, true); + int32_t ret = FAIL; + JNIEnv* env = GetJNIEnv(); - sslStream->appOutBuffer = ToGRef(env, (*env)->CallStaticObjectMethod(env, g_ByteBuffer, g_ByteBufferAllocateMethod, appOutBufferSize)); - sslStream->netOutBuffer = ToGRef(env, (*env)->CallStaticObjectMethod(env, g_ByteBuffer, g_ByteBufferAllocateMethod, packetBufferSize)); - sslStream->appInBuffer = ToGRef(env, (*env)->CallStaticObjectMethod(env, g_ByteBuffer, g_ByteBufferAllocateMethod, - applicationBufferSize > appInBufferSize ? applicationBufferSize : appInBufferSize)); - sslStream->netInBuffer = ToGRef(env, (*env)->CallStaticObjectMethod(env, g_ByteBuffer, g_ByteBufferAllocateMethod, packetBufferSize)); + // SSLEngine sslEngine = sslContext.createSSLEngine(); + // sslEngine.setUseClientMode(!isServer); + jobject sslEngine = (*env)->CallObjectMethod(env, sslStream->sslContext, g_SSLContextCreateSSLEngineMethod); + ON_EXCEPTION_PRINT_AND_GOTO(exit); + sslStream->sslEngine = ToGRef(env, sslEngine); + (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineSetUseClientMode, !isServer); + ON_EXCEPTION_PRINT_AND_GOTO(exit); + + // SSLSession sslSession = sslEngine.getSession(); + sslStream->sslSession = ToGRef(env, (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetSession)); + + // int applicationBufferSize = sslSession.getApplicationBufferSize(); + // int packetBufferSize = sslSession.getPacketBufferSize(); + int32_t applicationBufferSize = + (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetApplicationBufferSize); + int32_t packetBufferSize = (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetPacketBufferSize); + + // ByteBuffer appInBuffer = ByteBuffer.allocate(Math.max(applicationBufferSize, appBufferSize)); + // ByteBuffer appOutBuffer = ByteBuffer.allocate(appBufferSize); + // ByteBuffer netOutBuffer = ByteBuffer.allocate(packetBufferSize); + // ByteBuffer netInBuffer = ByteBuffer.allocate(packetBufferSize); + int32_t appInBufferSize = applicationBufferSize > appBufferSize ? applicationBufferSize : appBufferSize; + sslStream->appInBuffer = + ToGRef(env, (*env)->CallStaticObjectMethod(env, g_ByteBuffer, g_ByteBufferAllocate, appInBufferSize)); + sslStream->appOutBuffer = + ToGRef(env, (*env)->CallStaticObjectMethod(env, g_ByteBuffer, g_ByteBufferAllocate, appBufferSize)); + sslStream->netOutBuffer = + ToGRef(env, (*env)->CallStaticObjectMethod(env, g_ByteBuffer, g_ByteBufferAllocate, packetBufferSize)); + sslStream->netInBuffer = + ToGRef(env, (*env)->CallStaticObjectMethod(env, g_ByteBuffer, g_ByteBufferAllocate, packetBufferSize)); sslStream->streamReader = streamReader; sslStream->streamWriter = streamWriter; - (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineBeginHandshakeMethod); + ret = SUCCESS; - checkHandshakeStatus(env, sslStream, getHandshakeStatus(env, sslStream, NULL)); - (*env)->DeleteLocalRef(env, tlsVerStr); - AssertOnJNIExceptions(env); - return sslStream; +exit: + return ret; } -int AndroidCryptoNative_SSLStreamRead(SSLStream* sslStream, uint8_t* buffer, int offset, int length) +int32_t AndroidCryptoNative_SSLStreamConfigureParameters(SSLStream* sslStream, char* targetHost) { + assert(sslStream != NULL); + assert(targetHost != NULL); + + JNIEnv* env = GetJNIEnv(); + + int32_t ret = FAIL; + INIT_LOCALS(loc, hostStr, nameList, hostName, params); + + // ArrayList nameList = new ArrayList(); + // SNIHostName hostName = new SNIHostName(targetHost); + // nameList.add(hostName); + loc[hostStr] = JSTRING(targetHost); + loc[nameList] = (*env)->NewObject(env, g_ArrayListClass, g_ArrayListCtor); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + loc[hostName] = (*env)->NewObject(env, g_SNIHostName, g_SNIHostNameCtor, loc[hostStr]); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + (*env)->CallBooleanMethod(env, loc[nameList], g_ArrayListAdd, loc[hostName]); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + // SSLParameters params = new SSLParameters(); + // params.setServerNames(nameList); + // sslEngine.setSSLParameters(params); + loc[params] = (*env)->NewObject(env, g_SSLParametersClass, g_SSLParametersCtor); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + (*env)->CallVoidMethod(env, loc[params], g_SSLParametersSetServerNames, loc[nameList]); + (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineSetSSLParameters, loc[params]); + + ret = SUCCESS; + +cleanup: + RELEASE_LOCALS(loc, env); + return ret; +} + +PAL_SSLStreamStatus AndroidCryptoNative_SSLStreamHandshake(SSLStream* sslStream) +{ + assert(sslStream != NULL); + JNIEnv* env = GetJNIEnv(); + + // sslEngine.beginHandshake(); + (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineBeginHandshake); + if (CheckJNIExceptions(env)) + return SSLStreamStatus_Error; + + return DoHandshake(env, sslStream); +} + +PAL_SSLStreamStatus +AndroidCryptoNative_SSLStreamRead(SSLStream* sslStream, uint8_t* buffer, int32_t length, int32_t* read) +{ + assert(sslStream != NULL); + assert(read != NULL); + + jbyteArray data = NULL; JNIEnv* env = GetJNIEnv(); + PAL_SSLStreamStatus ret = SSLStreamStatus_Error; + *read = 0; /* - while (true) { + appInBuffer.flip(); + if (appInBuffer.remaining() == 0) { + appInBuffer.compact(); + DoUnwrap(); appInBuffer.flip(); - try { - if (appInBuffer.remaining() > 0) { - byte[] data = new byte[appInBuffer.remaining()]; - appInBuffer.get(data); - return data; - } - } finally { - appInBuffer.compact(); - } - doUnwrap(); + } + if (appInBuffer.remaining() > 0) { + byte[] data = new byte[appInBuffer.remaining()]; + appInBuffer.get(data); + appInBuffer.compact(); + return SSLStreamStatus_OK; + } else { + return SSLStreamStatus_NeedData; } */ - AssertOnJNIExceptions(env); - (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->appInBuffer, g_ByteBufferFlipMethod)); - int rem = (*env)->CallIntMethod(env, sslStream->appInBuffer, g_ByteBufferRemainingMethod); + IGNORE_RETURN((*env)->CallObjectMethod(env, sslStream->appInBuffer, g_ByteBufferFlip)); + int32_t rem = (*env)->CallIntMethod(env, sslStream->appInBuffer, g_ByteBufferRemaining); + if (rem == 0) + { + IGNORE_RETURN((*env)->CallObjectMethod(env, sslStream->appInBuffer, g_ByteBufferCompact)); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + int handshakeStatus; + PAL_SSLStreamStatus unwrapStatus = DoUnwrap(env, sslStream, &handshakeStatus); + if (unwrapStatus != SSLStreamStatus_OK) + { + ret = unwrapStatus; + goto cleanup; + } + + IGNORE_RETURN((*env)->CallObjectMethod(env, sslStream->appInBuffer, g_ByteBufferFlip)); + + if (IsHandshaking(handshakeStatus)) + { + ret = SSLStreamStatus_Renegotiate; + goto cleanup; + } + + rem = (*env)->CallIntMethod(env, sslStream->appInBuffer, g_ByteBufferRemaining); + } + if (rem > 0) { - jbyteArray data = (*env)->NewByteArray(env, rem); - (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->appInBuffer, g_ByteBufferGetMethod, data)); - (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->appInBuffer, g_ByteBufferCompactMethod)); - (*env)->GetByteArrayRegion(env, data, 0, rem, (jbyte*) buffer); - AssertOnJNIExceptions(env); - return rem; + data = (*env)->NewByteArray(env, rem); + IGNORE_RETURN((*env)->CallObjectMethod(env, sslStream->appInBuffer, g_ByteBufferGet, data)); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + IGNORE_RETURN((*env)->CallObjectMethod(env, sslStream->appInBuffer, g_ByteBufferCompact)); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + (*env)->GetByteArrayRegion(env, data, 0, rem, (jbyte*)buffer); + *read = rem; + ret = SSLStreamStatus_OK; } else { - (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->appInBuffer, g_ByteBufferCompactMethod)); - doUnwrap(env, sslStream); - AssertOnJNIExceptions(env); - return AndroidCryptoNative_SSLStreamRead(sslStream, buffer, offset, length); + ret = SSLStreamStatus_NeedData; } + +cleanup: + ReleaseLRef(env, data); + return ret; } -void AndroidCryptoNative_SSLStreamWrite(SSLStream* sslStream, uint8_t* buffer, int offset, int length) +PAL_SSLStreamStatus AndroidCryptoNative_SSLStreamWrite(SSLStream* sslStream, uint8_t* buffer, int32_t length) { - /* - appOutBuffer.put(message); - doWrap(); - */ + assert(sslStream != NULL); JNIEnv* env = GetJNIEnv(); + PAL_SSLStreamStatus ret = SSLStreamStatus_Error; + + // byte[] data = new byte[] { } + // appOutBuffer.put(data); jbyteArray data = (*env)->NewByteArray(env, length); - (*env)->SetByteArrayRegion(env, data, 0, length, (jbyte*)(buffer + offset)); - (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->appOutBuffer, g_ByteBufferPut2Method, data)); + (*env)->SetByteArrayRegion(env, data, 0, length, (jbyte*)buffer); + IGNORE_RETURN((*env)->CallObjectMethod(env, sslStream->appOutBuffer, g_ByteBufferPutByteArray, data)); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + int handshakeStatus; + ret = DoWrap(env, sslStream, &handshakeStatus); + if (ret == SSLStreamStatus_OK && IsHandshaking(handshakeStatus)) + { + ret = SSLStreamStatus_Renegotiate; + } + +cleanup: (*env)->DeleteLocalRef(env, data); - doWrap(env, sslStream); - AssertOnJNIExceptions(env); + return ret; } void AndroidCryptoNative_SSLStreamRelease(SSLStream* sslStream) { + if (sslStream == NULL) + return; + JNIEnv* env = GetJNIEnv(); - ReleaseGRef(env, sslStream->sslContext); - ReleaseGRef(env, sslStream->sslEngine); - ReleaseGRef(env, sslStream->sslSession); - ReleaseGRef(env, sslStream->appOutBuffer); - ReleaseGRef(env, sslStream->netOutBuffer); - ReleaseGRef(env, sslStream->netInBuffer); - ReleaseGRef(env, sslStream->appInBuffer); - free(sslStream); - AssertOnJNIExceptions(env); + FreeSSLStream(env, sslStream); +} + +int32_t AndroidCryptoNative_SSLStreamGetApplicationProtocol(SSLStream* sslStream, uint8_t* out, int32_t* outLen) +{ + assert(sslStream != NULL); + + JNIEnv* env = GetJNIEnv(); + int32_t ret = FAIL; + + // String protocol = sslEngine.getApplicationProtocol(); + jstring protocol = (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetApplicationProtocol); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + if (protocol == NULL) + goto cleanup; + + jsize len = (*env)->GetStringUTFLength(env, protocol); + bool insufficientBuffer = *outLen < len; + *outLen = len; + if (insufficientBuffer) + return INSUFFICIENT_BUFFER; + + (*env)->GetStringUTFRegion(env, protocol, 0, len, (char*)out); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + ret = SUCCESS; + +cleanup: + (*env)->DeleteLocalRef(env, protocol); + return ret; +} + +int32_t AndroidCryptoNative_SSLStreamGetCipherSuite(SSLStream* sslStream, uint16_t** out) +{ + assert(sslStream != NULL); + assert(out != NULL); + + JNIEnv* env = GetJNIEnv(); + int32_t ret = FAIL; + *out = NULL; + + // String cipherSuite = sslSession.getCipherSuite(); + jstring cipherSuite = (*env)->CallObjectMethod(env, sslStream->sslSession, g_SSLSessionGetCipherSuite); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + *out = AllocateString(env, cipherSuite); + + ret = SUCCESS; + +cleanup: + (*env)->DeleteLocalRef(env, cipherSuite); + return ret; +} + +int32_t AndroidCryptoNative_SSLStreamGetProtocol(SSLStream* sslStream, uint16_t** out) +{ + assert(sslStream != NULL); + assert(out != NULL); + + JNIEnv* env = GetJNIEnv(); + int32_t ret = FAIL; + *out = NULL; + + // String protocol = sslSession.getProtocol(); + jstring protocol = (*env)->CallObjectMethod(env, sslStream->sslSession, g_SSLSessionGetProtocol); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + *out = AllocateString(env, protocol); + + ret = SUCCESS; + +cleanup: + (*env)->DeleteLocalRef(env, protocol); + return ret; +} + +int32_t AndroidCryptoNative_SSLStreamGetPeerCertificate(SSLStream* sslStream, jobject* out) +{ + assert(sslStream != NULL); + assert(out != NULL); + + JNIEnv* env = GetJNIEnv(); + int32_t ret = FAIL; + *out = NULL; + + // Certificate[] certs = sslSession.getPeerCertificates(); + // out = certs[0]; + jobjectArray certs = (*env)->CallObjectMethod(env, sslStream->sslSession, g_SSLSessionGetPeerCertificates); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + jsize len = (*env)->GetArrayLength(env, certs); + if (len > 0) + { + // First element is the peer's own certificate + jobject cert = (*env)->GetObjectArrayElement(env, certs, 0); + *out = ToGRef(env, cert); + } + + ret = SUCCESS; + +cleanup: + (*env)->DeleteLocalRef(env, certs); + return ret; +} + +int32_t AndroidCryptoNative_SSLStreamGetPeerCertificates(SSLStream* sslStream, jobject** out, int32_t* outLen) +{ + assert(sslStream != NULL); + assert(out != NULL); + + JNIEnv* env = GetJNIEnv(); + int32_t ret = FAIL; + *out = NULL; + *outLen = 0; + + // Certificate[] certs = sslSession.getPeerCertificates(); + // for (int i = 0; i < certs.length; i++) { + // out[i] = certs[i]; + // } + jobjectArray certs = (*env)->CallObjectMethod(env, sslStream->sslSession, g_SSLSessionGetPeerCertificates); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + jsize len = (*env)->GetArrayLength(env, certs); + *outLen = len; + if (len > 0) + { + *out = malloc(sizeof(jobject) * (size_t)len); + for (int32_t i = 0; i < len; i++) + { + jobject cert = (*env)->GetObjectArrayElement(env, certs, i); + (*out)[i] = ToGRef(env, cert); + } + } + + ret = SUCCESS; + +cleanup: + (*env)->DeleteLocalRef(env, certs); + return ret; +} + +static jstring GetSslProtocolAsString(JNIEnv* env, PAL_SslProtocol protocol) +{ + switch (protocol) + { + case PAL_SslProtocol_Tls10: + return JSTRING("TLSv1"); + case PAL_SslProtocol_Tls11: + return JSTRING("TLSv1.1"); + case PAL_SslProtocol_Tls12: + return JSTRING("TLSv1.2"); + case PAL_SslProtocol_Tls13: + return JSTRING("TLSv1.3"); + default: + LOG_ERROR("Unsupported SslProtocols value: %d", protocol); + return NULL; + } +} + +int32_t +AndroidCryptoNative_SSLStreamSetEnabledProtocols(SSLStream* sslStream, PAL_SslProtocol* protocols, int32_t count) +{ + assert(sslStream != NULL); + + JNIEnv* env = GetJNIEnv(); + int32_t ret = FAIL; + + // String[] protocolsArray = new String[count]; + jobjectArray protocolsArray = (*env)->NewObjectArray(env, count, g_String, NULL); + for (int32_t i = 0; i < count; ++i) + { + jstring protocol = GetSslProtocolAsString(env, protocols[i]); + (*env)->SetObjectArrayElement(env, protocolsArray, i, protocol); + (*env)->DeleteLocalRef(env, protocol); + } + + // sslEngine.setEnabledProtocols(protocolsArray); + (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineSetEnabledProtocols, protocolsArray); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + ret = SUCCESS; + +cleanup: + (*env)->DeleteLocalRef(env, protocolsArray); + return ret; +} + +bool AndroidCryptoNative_SSLStreamVerifyHostname(SSLStream* sslStream, char* hostname) +{ + assert(sslStream != NULL); + assert(hostname != NULL); + JNIEnv* env = GetJNIEnv(); + + bool ret = false; + INIT_LOCALS(loc, name, verifier); + + // HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier(); + // return verifier.verify(hostname, sslSession); + loc[name] = JSTRING(hostname); + loc[verifier] = + (*env)->CallStaticObjectMethod(env, g_HttpsURLConnection, g_HttpsURLConnectionGetDefaultHostnameVerifier); + ret = (*env)->CallBooleanMethod(env, loc[verifier], g_HostnameVerifierVerify, loc[name], sslStream->sslSession); + + RELEASE_LOCALS(loc, env); + return ret; +} + +bool AndroidCryptoNative_SSLStreamShutdown(SSLStream* sslStream) +{ + assert(sslStream != NULL); + JNIEnv* env = GetJNIEnv(); + + PAL_SSLStreamStatus status = Close(env, sslStream); + return status == SSLStreamStatus_Closed; +} + +static uint16_t* AllocateString(JNIEnv* env, jstring source) +{ + if (source == NULL) + return NULL; + + // Length with null terminator + jsize len = (*env)->GetStringLength(env, source); + + // +1 for null terminator. + uint16_t* buffer = malloc(sizeof(uint16_t) * (size_t)(len + 1)); + buffer[len] = '\0'; + + (*env)->GetStringRegion(env, source, 0, len, (jchar*)buffer); + return buffer; } diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_sslstream.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_sslstream.h index 348760cfdc0a0a..fadd70ae15577c 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_sslstream.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_sslstream.h @@ -4,9 +4,12 @@ #pragma once #include "pal_jni.h" +#include "pal_x509.h" -typedef void (*STREAM_WRITER)(uint8_t*, uint32_t, uint32_t); -typedef int (*STREAM_READER)(uint8_t*, uint32_t, uint32_t); +#include + +typedef void (*STREAM_WRITER)(uint8_t*, int32_t); +typedef int32_t (*STREAM_READER)(uint8_t*, int32_t*); typedef struct SSLStream { @@ -21,24 +24,147 @@ typedef struct SSLStream STREAM_WRITER streamWriter; } SSLStream; -#define TLS11 11 -#define TLS12 12 -#define TLS13 13 - -// javax/net/ssl/SSLEngineResult$HandshakeStatus -#define HANDSHAKE_STATUS__NOT_HANDSHAKING 0 -#define HANDSHAKE_STATUS__FINISHED 1 -#define HANDSHAKE_STATUS__NEED_TASK 2 -#define HANDSHAKE_STATUS__NEED_WRAP 3 -#define HANDSHAKE_STATUS__NEED_UNWRAP 4 - -// javax/net/ssl/SSLEngineResult$Status -#define STATUS__BUFFER_UNDERFLOW 0 -#define STATUS__BUFFER_OVERFLOW 1 -#define STATUS__OK 2 -#define STATUS__CLOSED 3 - -PALEXPORT SSLStream* AndroidCryptoNative_SSLStreamCreateAndStartHandshake(STREAM_READER streamReader, STREAM_WRITER streamWriter, int tlsVersion, int appOutBufferSize, int appInBufferSize); -PALEXPORT int AndroidCryptoNative_SSLStreamRead(SSLStream* sslStream, uint8_t* buffer, int offset, int length); -PALEXPORT void AndroidCryptoNative_SSLStreamWrite(SSLStream* sslStream, uint8_t* buffer, int offset, int length); +// Matches managed PAL_SSLStreamStatus enum +enum +{ + SSLStreamStatus_OK = 0, + SSLStreamStatus_NeedData = 1, + SSLStreamStatus_Error = 2, + SSLStreamStatus_Renegotiate = 3, + SSLStreamStatus_Closed = 4, +}; +typedef int32_t PAL_SSLStreamStatus; + +/* +Create an SSL context + +Returns NULL on failure +*/ +PALEXPORT SSLStream* AndroidCryptoNative_SSLStreamCreate(void); + +/* +Create an SSL context with the specified certificates + +Returns NULL on failure +*/ +PALEXPORT SSLStream* AndroidCryptoNative_SSLStreamCreateWithCertificates(uint8_t* pkcs8PrivateKey, + int32_t pkcs8PrivateKeyLen, + PAL_KeyAlgorithm algorithm, + jobject* /*X509Certificate[]*/ certs, + int32_t certsLen); + +/* +Initialize an SSL context + - isServer : true if the context should be created in server mode + - streamReader : callback for reading data from the connection + - streamWriter : callback for writing data to the connection + - appBufferSize : initial buffer size for applicaiton data + +Returns 1 on success, 0 otherwise +*/ +PALEXPORT int32_t AndroidCryptoNative_SSLStreamInitialize( + SSLStream* sslStream, bool isServer, STREAM_READER streamReader, STREAM_WRITER streamWriter, int32_t appBufferSize); + +/* +Set configuration parameters + - targetHost : SNI host name + +Returns 1 on success, 0 otherwise +*/ +PALEXPORT int32_t AndroidCryptoNative_SSLStreamConfigureParameters(SSLStream* sslStream, char* targetHost); + +/* +Start or continue the TLS handshake +*/ +PALEXPORT PAL_SSLStreamStatus AndroidCryptoNative_SSLStreamHandshake(SSLStream* sslStream); + +/* +Read bytes from the connection into a buffer + - buffer : buffer to populate with the bytes read from the connection + - length : maximum number of bytes to read + - read : [out] number of bytes read from the connection and written into the buffer + +Unless data from a previous incomplete read is present, this will invoke the STREAM_READER callback. +*/ +PALEXPORT PAL_SSLStreamStatus AndroidCryptoNative_SSLStreamRead(SSLStream* sslStream, + uint8_t* buffer, + int32_t length, + int32_t* read); +/* +Encodes bytes from a buffer + - buffer : data to encode + - length : length of buffer + +This will invoke the STREAM_WRITER callback with the processed data. +*/ +PALEXPORT PAL_SSLStreamStatus AndroidCryptoNative_SSLStreamWrite(SSLStream* sslStream, uint8_t* buffer, int32_t length); + +/* +Release the SSL context +*/ PALEXPORT void AndroidCryptoNative_SSLStreamRelease(SSLStream* sslStream); + +/* +Get the negotiated application protocol for the current session + +Returns 1 on success, 0 otherwise +*/ +PALEXPORT int32_t AndroidCryptoNative_SSLStreamGetApplicationProtocol(SSLStream* sslStream, + uint8_t* out, + int32_t* outLen); + +/* +Get the name of the cipher suite for the current session + +Returns 1 on success, 0 otherwise +*/ +PALEXPORT int32_t AndroidCryptoNative_SSLStreamGetCipherSuite(SSLStream* sslStream, uint16_t** out); + +/* +Get the standard name of the protocol for the current session (e.g. TLSv1.2) + +Returns 1 on success, 0 otherwise +*/ +PALEXPORT int32_t AndroidCryptoNative_SSLStreamGetProtocol(SSLStream* sslStream, uint16_t** out); + +/* +Get the peer certificate for the current session + +Returns 1 on success, 0 otherwise +*/ +PALEXPORT int32_t AndroidCryptoNative_SSLStreamGetPeerCertificate(SSLStream* sslStream, + jobject* /*X509Certificate*/ out); + +/* +Get the peer certificates for the current session + +The peer's own certificate will be first, followed by any certificate authorities. + +Returns 1 on success, 0 otherwise +*/ +PALEXPORT int32_t AndroidCryptoNative_SSLStreamGetPeerCertificates(SSLStream* sslStream, + jobject** /*X509Certificate[]*/ out, + int32_t* outLen); + +/* +Set enabled protocols + - protocols : array of protocols to enable + - count : number of elements in protocols + +Returns 1 on success, 0 otherwise +*/ +PALEXPORT int32_t AndroidCryptoNative_SSLStreamSetEnabledProtocols(SSLStream* sslStream, + PAL_SslProtocol* protocols, + int32_t count); + +/* +Verify hostname using the peer certificate for the current session + +Returns true if hostname matches, false otherwise +*/ +PALEXPORT bool AndroidCryptoNative_SSLStreamVerifyHostname(SSLStream* sslStream, char* hostname); + +/* +Shut down the session +*/ +PALEXPORT bool AndroidCryptoNative_SSLStreamShutdown(SSLStream* sslStream); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_x509.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_x509.c index dfa72406a6c5ed..f7dc194895b955 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_x509.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_x509.c @@ -12,8 +12,6 @@ #include #include -#define INSUFFICIENT_BUFFER -1 - static int32_t PopulateByteArray(JNIEnv* env, jbyteArray source, uint8_t* dest, int32_t* len); static void FindCertStart(const uint8_t** buffer, int32_t* len); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_x509store.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_x509store.c index 64b839d2018d3b..9724e8af2f4afa 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_x509store.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_x509store.c @@ -329,7 +329,7 @@ int32_t AndroidCryptoNative_X509StoreEnumerateCertificates(jobject /*KeyStore*/ static bool SystemAliasFilter(JNIEnv* env, jstring alias) { const char systemPrefix[] = "system:"; - size_t prefixLen = (sizeof(systemPrefix) - 1) / sizeof(char); + size_t prefixLen = (sizeof(systemPrefix) / sizeof(*systemPrefix)) - 1; const char* aliasPtr = (*env)->GetStringUTFChars(env, alias, NULL); bool isSystem = (strncmp(aliasPtr, systemPrefix, prefixLen) == 0); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_ssl.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_ssl.h index 518ef35c67f388..8c3b61d530f833 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_ssl.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_ssl.h @@ -4,6 +4,7 @@ #pragma once #include "pal_compiler.h" +#include #include #include @@ -27,18 +28,6 @@ enum }; typedef int32_t PAL_TlsIo; -enum -{ - PAL_SslProtocol_None = 0, - PAL_SslProtocol_Ssl2 = 12, - PAL_SslProtocol_Ssl3 = 48, - PAL_SslProtocol_Tls10 = 192, - PAL_SslProtocol_Tls11 = 768, - PAL_SslProtocol_Tls12 = 3072, - PAL_SslProtocol_Tls13 = 12288, -}; -typedef int32_t PAL_SslProtocol; - /* Create an SSL context, for the Server or Client role as determined by isServer. diff --git a/src/libraries/Native/build-native.sh b/src/libraries/Native/build-native.sh index 55d56a7099c353..f95f1f622f0e41 100755 --- a/src/libraries/Native/build-native.sh +++ b/src/libraries/Native/build-native.sh @@ -104,57 +104,49 @@ elif [[ "$__TargetOS" == Android && -z "$ROOTFS_DIR" ]]; then exit 1 fi elif [[ "$__TargetOS" == iOSSimulator ]]; then - __CMakeArgs="-DCMAKE_SYSTEM_NAME=iOS $__CMakeArgs" + # set default iOS simulator deployment target + # keep in sync with src/mono/Directory.Build.props + __CMakeArgs="-DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_SYSROOT=iphonesimulator -DCMAKE_OSX_DEPLOYMENT_TARGET=10.0 $__CMakeArgs" if [[ "$__BuildArch" == x64 ]]; then - # set default iOS simulator deployment target (8.0 is the minimum supported by Xcode 11) - # keep in sync with src/mono/Directory.Build.props - __CMakeArgs="-DCMAKE_OSX_SYSROOT=iphonesimulator -DCMAKE_OSX_DEPLOYMENT_TARGET=8.0 -DCMAKE_OSX_ARCHITECTURES=\"x86_64\" $__CMakeArgs" + __CMakeArgs="-DCMAKE_OSX_ARCHITECTURES=\"x86_64\" $__CMakeArgs" elif [[ "$__BuildArch" == x86 ]]; then - # set default iOS simulator deployment target (8.0 is the minimum supported by Xcode 11) - # keep in sync with src/mono/Directory.Build.props - __CMakeArgs="-DCMAKE_OSX_SYSROOT=iphonesimulator -DCMAKE_OSX_DEPLOYMENT_TARGET=8.0 -DCMAKE_OSX_ARCHITECTURES=\"i386\" $__CMakeArgs" + __CMakeArgs="-DCMAKE_OSX_ARCHITECTURES=\"i386\" $__CMakeArgs" elif [[ "$__BuildArch" == arm64 ]]; then - # set default iOS device deployment target - # keep in sync with src/mono/Directory.Build.props - __CMakeArgs="-DCMAKE_OSX_SYSROOT=iphonesimulator -DCMAKE_OSX_DEPLOYMENT_TARGET=8.0 -DCMAKE_OSX_ARCHITECTURES=\"arm64\" $__CMakeArgs" + __CMakeArgs="-DCMAKE_OSX_ARCHITECTURES=\"arm64\" $__CMakeArgs" else echo "Error: Unknown iOSSimulator architecture $__BuildArch." exit 1 fi elif [[ "$__TargetOS" == iOS ]]; then - __CMakeArgs="-DCMAKE_SYSTEM_NAME=iOS $__CMakeArgs" + # set default iOS device deployment target + # keep in sync with src/mono/Directory.Build.props + __CMakeArgs="-DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_SYSROOT=iphoneos -DCMAKE_OSX_DEPLOYMENT_TARGET=10.0 $__CMakeArgs" if [[ "$__BuildArch" == arm64 ]]; then - # set default iOS device deployment target - # keep in sync with src/mono/Directory.Build.props - __CMakeArgs="-DCMAKE_OSX_SYSROOT=iphoneos -DCMAKE_OSX_DEPLOYMENT_TARGET=8.0 -DCMAKE_OSX_ARCHITECTURES=\"arm64\" $__CMakeArgs" + __CMakeArgs="-DCMAKE_OSX_ARCHITECTURES=\"arm64\" $__CMakeArgs" elif [[ "$__BuildArch" == arm ]]; then - # set default iOS device deployment target - # keep in sync with src/mono/Directory.Build.props - __CMakeArgs="-DCMAKE_OSX_SYSROOT=iphoneos -DCMAKE_OSX_DEPLOYMENT_TARGET=8.0 -DCMAKE_OSX_ARCHITECTURES=\"armv7;armv7s\" $__CMakeArgs" + __CMakeArgs="-DCMAKE_OSX_ARCHITECTURES=\"armv7;armv7s\" $__CMakeArgs" else echo "Error: Unknown iOS architecture $__BuildArch." exit 1 fi elif [[ "$__TargetOS" == tvOSSimulator ]]; then - __CMakeArgs="-DCMAKE_SYSTEM_NAME=tvOS $__CMakeArgs" - # set default tvOS device deployment target - # keep in sync with tvOSVersionMin in src/mono/Directory.Build.props - __CMakeArgs="-DCMAKE_OSX_DEPLOYMENT_TARGET=9.0 $__CMakeArgs" + # set default tvOS simulator deployment target + # keep in sync with src/mono/Directory.Build.props + __CMakeArgs="-DCMAKE_SYSTEM_NAME=tvOS -DCMAKE_OSX_SYSROOT=appletvsimulator -DCMAKE_OSX_DEPLOYMENT_TARGET=10.0 $__CMakeArgs" if [[ "$__BuildArch" == x64 ]]; then - __CMakeArgs="-DCMAKE_OSX_SYSROOT=appletvsimulator -DCMAKE_OSX_ARCHITECTURES=\"x86_64\" $__CMakeArgs" + __CMakeArgs="-DCMAKE_OSX_ARCHITECTURES=\"x86_64\" $__CMakeArgs" elif [[ "$__BuildArch" == arm64 ]]; then - __CMakeArgs="-DCMAKE_OSX_SYSROOT=appletvsimulator -DCMAKE_OSX_ARCHITECTURES=\"arm64\" $__CMakeArgs" + __CMakeArgs="-DCMAKE_OSX_ARCHITECTURES=\"arm64\" $__CMakeArgs" else echo "Error: Unknown tvOSSimulator architecture $__BuildArch." exit 1 fi elif [[ "$__TargetOS" == tvOS ]]; then - __CMakeArgs="-DCMAKE_SYSTEM_NAME=tvOS $__CMakeArgs" - # set default tvOS device deployment target - # keep in sync with tvOSVersionMin in src/mono/Directory.Build.props - __CMakeArgs="-DCMAKE_OSX_DEPLOYMENT_TARGET=9.0 $__CMakeArgs" + # set default tvOS device deployment target + # keep in sync with src/mono/Directory.Build.props + __CMakeArgs="-DCMAKE_SYSTEM_NAME=tvOS -DCMAKE_OSX_SYSROOT=appletvos -DCMAKE_OSX_DEPLOYMENT_TARGET=10.0 $__CMakeArgs" if [[ "$__BuildArch" == arm64 ]]; then - __CMakeArgs="-DCMAKE_OSX_SYSROOT=appletvos -DCMAKE_OSX_ARCHITECTURES=\"arm64\" $__CMakeArgs" + __CMakeArgs="-DCMAKE_OSX_ARCHITECTURES=\"arm64\" $__CMakeArgs" else echo "Error: Unknown tvOS architecture $__BuildArch." exit 1 diff --git a/src/libraries/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/DirectoryCatalogDebuggerProxyTests.cs b/src/libraries/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/DirectoryCatalogDebuggerProxyTests.cs index 71b71ec0a749b5..c24674d7212a54 100644 --- a/src/libraries/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/DirectoryCatalogDebuggerProxyTests.cs +++ b/src/libraries/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/DirectoryCatalogDebuggerProxyTests.cs @@ -126,7 +126,7 @@ public void LoadedFiles_EmptyDirectory_ShouldBeFine() } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public void LoadedFiles_ContainsMultipleDllsAndSomeNonDll_ShouldOnlyContainDlls() { string directoryPath = GetTemporaryDirectory(); diff --git a/src/libraries/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/DirectoryCatalogTests.cs b/src/libraries/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/DirectoryCatalogTests.cs index ec9821debf3891..342d8412347ef6 100644 --- a/src/libraries/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/DirectoryCatalogTests.cs +++ b/src/libraries/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/DirectoryCatalogTests.cs @@ -325,7 +325,7 @@ public void LoadedFiles_EmptyDirectory_ShouldBeFine() } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public void LoadedFiles_ContainsMultipleDllsAndSomeNonDll_ShouldOnlyContainDlls() { // Add one text file diff --git a/src/libraries/System.ComponentModel.Primitives/src/System/ComponentModel/CategoryAttribute.cs b/src/libraries/System.ComponentModel.Primitives/src/System/ComponentModel/CategoryAttribute.cs index 976155e8336b5e..e9c6414aaf311d 100644 --- a/src/libraries/System.ComponentModel.Primitives/src/System/ComponentModel/CategoryAttribute.cs +++ b/src/libraries/System.ComponentModel.Primitives/src/System/ComponentModel/CategoryAttribute.cs @@ -197,7 +197,7 @@ public override bool Equals(object? obj) => /// /// Looks up the localized name of a given category. /// - protected virtual string? GetLocalizedString(string value) => SR.GetResourceString("PropertyCategory" + value, null); + protected virtual string? GetLocalizedString(string value) => SR.GetResourceString("PropertyCategory" + value); public override bool IsDefaultAttribute() => Category == Default.Category; } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/Timers/TimersDescriptionAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/Timers/TimersDescriptionAttribute.cs index 331abf43119536..1f991215906416 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/Timers/TimersDescriptionAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/Timers/TimersDescriptionAttribute.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.ComponentModel; +using System.Diagnostics; namespace System.Timers { @@ -23,7 +24,11 @@ public TimersDescriptionAttribute(string description) : base(description) { } /// /// Constructs a new localized sys description. /// - internal TimersDescriptionAttribute(string description, string defaultValue) : base(SR.GetResourceString(description, defaultValue)) { } + internal TimersDescriptionAttribute(string description, string unused) : base(SR.GetResourceString(description)) + { + // Needed for overload resolution + Debug.Assert(unused == null); + } /// /// Retrieves the description text. diff --git a/src/libraries/System.ComponentModel.TypeConverter/tests/Security/Authentication/ExtendedProtection/ExtendedProtectionPolicyTypeConverterTests.cs b/src/libraries/System.ComponentModel.TypeConverter/tests/Security/Authentication/ExtendedProtection/ExtendedProtectionPolicyTypeConverterTests.cs index a09110342ff6d3..82676a7f3f6e89 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/tests/Security/Authentication/ExtendedProtection/ExtendedProtectionPolicyTypeConverterTests.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/tests/Security/Authentication/ExtendedProtection/ExtendedProtectionPolicyTypeConverterTests.cs @@ -30,14 +30,14 @@ public void CanConvertTo_PositiveTests() } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] // System.Net.Security is not supported on this platform. + [SkipOnPlatform(TestPlatforms.Browser, "System.Net.Security is not supported on this platform.")] public void ConvertTo_NullTypeTests() { Assert.Throws(() => converter.ConvertTo(null, CultureInfo.InvariantCulture, new ExtendedProtectionPolicy(PolicyEnforcement.Never), null)); } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] // System.Net.Security is not supported on this platform. + [SkipOnPlatform(TestPlatforms.Browser, "System.Net.Security is not supported on this platform.")] public void ConvertTo_PositiveTests() { ExtendedProtectionPolicy policy = new ExtendedProtectionPolicy(PolicyEnforcement.Never); @@ -57,7 +57,7 @@ public void ConvertTo_PositiveTests() } [Theory] - [PlatformSpecific(~TestPlatforms.Browser)] // System.Net.Security is not supported on this platform. + [SkipOnPlatform(TestPlatforms.Browser, "System.Net.Security is not supported on this platform.")] [InlineData(typeof(int))] [InlineData(typeof(ExtendedProtectionPolicy))] [InlineData(typeof(bool))] diff --git a/src/libraries/System.Configuration.ConfigurationManager/tests/AssemblyInfo.cs b/src/libraries/System.Configuration.ConfigurationManager/tests/AssemblyInfo.cs index cd9251d1f14500..024dadef049771 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/tests/AssemblyInfo.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/tests/AssemblyInfo.cs @@ -4,4 +4,4 @@ using System; using Xunit; -[assembly: SkipOnMono("System.Configuration.ConfigurationManager is not supported on WASM", TestPlatforms.Browser)] +[assembly: SkipOnPlatform(TestPlatforms.Browser, "System.Configuration.ConfigurationManager is not supported on Browser")] diff --git a/src/libraries/System.Configuration.ConfigurationManager/tests/System.Configuration.ConfigurationManager.Tests.csproj b/src/libraries/System.Configuration.ConfigurationManager/tests/System.Configuration.ConfigurationManager.Tests.csproj index 94b27837000fbb..7c5067757fddd5 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/tests/System.Configuration.ConfigurationManager.Tests.csproj +++ b/src/libraries/System.Configuration.ConfigurationManager/tests/System.Configuration.ConfigurationManager.Tests.csproj @@ -5,6 +5,7 @@ true true $(NetCoreAppCurrent)-windows;net461 + true diff --git a/src/libraries/System.Console/tests/Color.cs b/src/libraries/System.Console/tests/Color.cs index 5ad7a05593aede..2cc8c585140928 100644 --- a/src/libraries/System.Console/tests/Color.cs +++ b/src/libraries/System.Console/tests/Color.cs @@ -12,7 +12,7 @@ public class Color { [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public static void InvalidColors() { AssertExtensions.Throws(null, () => Console.BackgroundColor = (ConsoleColor)42); @@ -20,7 +20,7 @@ public static void InvalidColors() } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public static void RoundtrippingColor() { Console.BackgroundColor = Console.BackgroundColor; @@ -49,7 +49,7 @@ public static void BackgroundColor_Throws_PlatformNotSupportedException() } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public static void RedirectedOutputDoesNotUseAnsiSequences() { // Make sure that redirecting to a memory stream causes Console not to write out the ANSI sequences diff --git a/src/libraries/System.Console/tests/ConsoleEncoding.cs b/src/libraries/System.Console/tests/ConsoleEncoding.cs index 99ec36447f59c2..d52d93c9282201 100644 --- a/src/libraries/System.Console/tests/ConsoleEncoding.cs +++ b/src/libraries/System.Console/tests/ConsoleEncoding.cs @@ -22,7 +22,7 @@ public static IEnumerable InputData() [Theory] [MemberData(nameof(InputData))] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public void TestEncoding(string inputString) { TextWriter outConsoleStream = Console.Out; @@ -79,7 +79,7 @@ public void TestEncoding(string inputString) } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public void TestValidEncodings() { Action check = encoding => diff --git a/src/libraries/System.Console/tests/ReadAndWrite.cs b/src/libraries/System.Console/tests/ReadAndWrite.cs index 7076c46edd9547..c4a13b21b644e0 100644 --- a/src/libraries/System.Console/tests/ReadAndWrite.cs +++ b/src/libraries/System.Console/tests/ReadAndWrite.cs @@ -258,7 +258,7 @@ private static unsafe void ValidateConsoleEncoding(Encoding encoding) } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public static unsafe void OutputEncodingPreamble() { Encoding curEncoding = Console.OutputEncoding; @@ -281,7 +281,7 @@ public static unsafe void OutputEncodingPreamble() } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public static unsafe void OutputEncoding() { Encoding curEncoding = Console.OutputEncoding; @@ -349,7 +349,7 @@ public static void InputEncoding_Getter_Throws_PlatformNotSupportedException() }; [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public static void ReadAndReadLine() { TextWriter savedStandardOutput = Console.Out; diff --git a/src/libraries/System.Console/tests/SetIn.cs b/src/libraries/System.Console/tests/SetIn.cs index 3531a7ec29ed32..46780d62e428e6 100644 --- a/src/libraries/System.Console/tests/SetIn.cs +++ b/src/libraries/System.Console/tests/SetIn.cs @@ -11,7 +11,7 @@ public class SetIn { [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public static void SetInThrowsOnNull() { TextReader savedIn = Console.In; @@ -26,7 +26,7 @@ public static void SetInThrowsOnNull() } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public static void SetInReadLine() { const string TextStringFormat = "Test {0}"; diff --git a/src/libraries/System.Console/tests/SyncTextReader.cs b/src/libraries/System.Console/tests/SyncTextReader.cs index 699bda4948db10..3a5c0b5617b17f 100644 --- a/src/libraries/System.Console/tests/SyncTextReader.cs +++ b/src/libraries/System.Console/tests/SyncTextReader.cs @@ -78,7 +78,7 @@ private static void Test(string content, Action action) } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public void ReadToEnd() { var expected = string.Join(Environment.NewLine, s_testLines); @@ -93,7 +93,7 @@ public void ReadToEnd() } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public void ReadBlock() { var expected = new[] { 'H', 'e', 'l', 'l', 'o' }; @@ -112,7 +112,7 @@ public void ReadBlock() } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public void Read() { var expected = new[] { 'H', 'e', 'l', 'l', 'o' }; @@ -131,7 +131,7 @@ public void Read() } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public void Peek() { const string expected = "ABC"; @@ -145,7 +145,7 @@ public void Peek() } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public void ReadToEndAsync() { var expected = string.Join(Environment.NewLine, s_testLines); @@ -160,7 +160,7 @@ public void ReadToEndAsync() } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public void ReadBlockAsync() { var expected = new[] { 'H', 'e', 'l', 'l', 'o' }; @@ -185,7 +185,7 @@ public void ReadBlockAsync() } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public void ReadAsync() { var expected = new[] { 'H', 'e', 'l', 'l', 'o' }; @@ -210,7 +210,7 @@ public void ReadAsync() } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public void ReadLineAsync() { var expected = string.Join(Environment.NewLine, s_testLines); diff --git a/src/libraries/System.Console/tests/TermInfo.cs b/src/libraries/System.Console/tests/TermInfo.cs index b798f61da3d578..57962fc199b6ff 100644 --- a/src/libraries/System.Console/tests/TermInfo.cs +++ b/src/libraries/System.Console/tests/TermInfo.cs @@ -7,7 +7,7 @@ using System.Reflection; using Xunit; -[PlatformSpecific(~TestPlatforms.Browser)] +[SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public class TermInfo { // Names of internal members accessed via reflection diff --git a/src/libraries/System.Console/tests/ThreadSafety.cs b/src/libraries/System.Console/tests/ThreadSafety.cs index e64e39235d68d3..ddc074760701e8 100644 --- a/src/libraries/System.Console/tests/ThreadSafety.cs +++ b/src/libraries/System.Console/tests/ThreadSafety.cs @@ -12,7 +12,7 @@ public class ThreadSafety const int NumberOfIterations = 100; [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public static void OpenStandardXXXCanBeCalledConcurrently() { Parallel.For(0, NumberOfIterations, i => @@ -41,7 +41,7 @@ public static void OpenStandardXXXCanBeCalledConcurrently() } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public static void SetStandardXXXCanBeCalledConcurrently() { TextReader savedStandardInput = Console.In; @@ -84,7 +84,7 @@ public static void SetStandardXXXCanBeCalledConcurrently() } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public static void ReadMayBeCalledConcurrently() { const char TestChar = '+'; diff --git a/src/libraries/System.Console/tests/Timeout.cs b/src/libraries/System.Console/tests/Timeout.cs index cac8723a22febd..f8630073ed55ff 100644 --- a/src/libraries/System.Console/tests/Timeout.cs +++ b/src/libraries/System.Console/tests/Timeout.cs @@ -11,7 +11,7 @@ public class TimeOut { [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public static void OpenStandardXXX_WriteTimeOut() { using (Stream standardOut = Console.OpenStandardOutput(), standardIn = Console.OpenStandardInput(), standardError = Console.OpenStandardError()) @@ -27,7 +27,7 @@ public static void OpenStandardXXX_WriteTimeOut() } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public static void OpenStandardXXX_ReadTimeOut() { using (Stream standardOut = Console.OpenStandardOutput(), standardIn = Console.OpenStandardInput(), standardError = Console.OpenStandardError()) @@ -43,7 +43,7 @@ public static void OpenStandardXXX_ReadTimeOut() } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public static void OpenStandardXXX_CanTimeOut() { using (Stream standardOut = Console.OpenStandardOutput(), standardIn = Console.OpenStandardInput(), standardError = Console.OpenStandardError()) diff --git a/src/libraries/System.Console/tests/WindowAndCursorProps.cs b/src/libraries/System.Console/tests/WindowAndCursorProps.cs index 1da4ce47340ab6..32df8909e11b64 100644 --- a/src/libraries/System.Console/tests/WindowAndCursorProps.cs +++ b/src/libraries/System.Console/tests/WindowAndCursorProps.cs @@ -337,7 +337,7 @@ public static void SetCursorPosition_Throws_PlatformNotSupportedException() } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public static void SetCursorPosition_Invoke_Success() { if (!OperatingSystem.IsWindows() || (!Console.IsInputRedirected && !Console.IsOutputRedirected)) @@ -364,7 +364,7 @@ public void SetCursorPosition_InvalidPosition_ThrowsArgumentOutOfRangeException( } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public static void GetCursorPosition_Invoke_ReturnsExpected() { if (!Console.IsInputRedirected && !Console.IsOutputRedirected) @@ -391,7 +391,7 @@ public static void GetCursorPosition_Invoke_ReturnsExpected() } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public void CursorLeft_Set_GetReturnsExpected() { if (!Console.IsInputRedirected && !Console.IsOutputRedirected) @@ -413,7 +413,7 @@ public void CursorLeft_Set_GetReturnsExpected() [Theory] [InlineData(-1)] [InlineData(short.MaxValue + 1)] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public void CursorLeft_SetInvalid_ThrowsArgumentOutOfRangeException(int value) { if (PlatformDetection.IsWindows && Console.IsOutputRedirected) @@ -434,7 +434,7 @@ public void CursorLeft_Setter_Throws_PlatformNotSupportedException() } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public void CursorTop_Set_GetReturnsExpected() { if (!Console.IsInputRedirected && !Console.IsOutputRedirected) @@ -456,7 +456,7 @@ public void CursorTop_Set_GetReturnsExpected() [Theory] [InlineData(-1)] [InlineData(short.MaxValue + 1)] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public void CursorTop_SetInvalid_ThrowsArgumentOutOfRangeException(int value) { if (PlatformDetection.IsWindows & Console.IsOutputRedirected) diff --git a/src/libraries/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.cs b/src/libraries/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.cs index 2537fb30e95dba..b758b3967c62ee 100644 --- a/src/libraries/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.cs +++ b/src/libraries/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.cs @@ -15,7 +15,7 @@ public sealed class TestProviderFactory : DbProviderFactory private TestProviderFactory() { } } - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public class DbProviderFactoriesTests { [Fact] diff --git a/src/libraries/System.Data.Common/tests/System/Data/Common/DbProviderFactoryTest.cs b/src/libraries/System.Data.Common/tests/System/Data/Common/DbProviderFactoryTest.cs index b810686ab4f72d..92f2cb69b0bfae 100644 --- a/src/libraries/System.Data.Common/tests/System/Data/Common/DbProviderFactoryTest.cs +++ b/src/libraries/System.Data.Common/tests/System/Data/Common/DbProviderFactoryTest.cs @@ -6,7 +6,7 @@ namespace System.Data.Tests.Common { - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public class DbProviderFactoryTest { [Fact] diff --git a/src/libraries/System.Data.Common/tests/System/Data/DataTableExtensionsTest.cs b/src/libraries/System.Data.Common/tests/System/Data/DataTableExtensionsTest.cs index 8608ba7bd645de..9d73511e9b07a9 100644 --- a/src/libraries/System.Data.Common/tests/System/Data/DataTableExtensionsTest.cs +++ b/src/libraries/System.Data.Common/tests/System/Data/DataTableExtensionsTest.cs @@ -5,7 +5,7 @@ namespace System.Data.Tests { - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public class DataTableExtensionsTest { private DataTable _dt; diff --git a/src/libraries/System.Data.Common/tests/System/Data/FacadeTest.cs b/src/libraries/System.Data.Common/tests/System/Data/FacadeTest.cs index f8eba45d7f5c48..de9ce64a4c1ade 100644 --- a/src/libraries/System.Data.Common/tests/System/Data/FacadeTest.cs +++ b/src/libraries/System.Data.Common/tests/System/Data/FacadeTest.cs @@ -10,7 +10,7 @@ public class FacadeTest [Theory] [InlineData("Microsoft.SqlServer.Server.SqlMetaData")] // Type from System.Data.SqlClient [InlineData("System.Data.SqlTypes.SqlBytes")] // Type from System.Data.Common - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public void TestSystemData(string typeName) { // Verify that the type can be loaded via .NET Framework compat facade diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/tests/TestWithConfigSwitches/System.Diagnostics.DiagnosticSource.Switches.Tests.csproj b/src/libraries/System.Diagnostics.DiagnosticSource/tests/TestWithConfigSwitches/System.Diagnostics.DiagnosticSource.Switches.Tests.csproj index 1d596d074a3ad4..ea4a7ff67293d5 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/tests/TestWithConfigSwitches/System.Diagnostics.DiagnosticSource.Switches.Tests.csproj +++ b/src/libraries/System.Diagnostics.DiagnosticSource/tests/TestWithConfigSwitches/System.Diagnostics.DiagnosticSource.Switches.Tests.csproj @@ -2,10 +2,9 @@ $(NetCoreAppCurrent) true - true + true - - \ No newline at end of file + diff --git a/src/libraries/System.Diagnostics.FileVersionInfo/src/System/Diagnostics/FileVersionInfo.Unix.cs b/src/libraries/System.Diagnostics.FileVersionInfo/src/System/Diagnostics/FileVersionInfo.Unix.cs index bf7eb505001380..f666e971e3f8ed 100644 --- a/src/libraries/System.Diagnostics.FileVersionInfo/src/System/Diagnostics/FileVersionInfo.Unix.cs +++ b/src/libraries/System.Diagnostics.FileVersionInfo/src/System/Diagnostics/FileVersionInfo.Unix.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Collections.Generic; using System.IO; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; @@ -14,6 +13,15 @@ private FileVersionInfo(string fileName) { _fileName = fileName; + // First make sure it's a file we can actually read from. Only regular files are relevant, + // and attempting to open and read from a file such as a named pipe file could cause us to + // stop responding (waiting for someone else to open and write to the file). + if (Interop.Sys.Stat(_fileName, out Interop.Sys.FileStatus fileStatus) != 0 || + (fileStatus.Mode & Interop.Sys.FileTypes.S_IFMT) != Interop.Sys.FileTypes.S_IFREG) + { + throw new FileNotFoundException(SR.Format(SR.IO_FileNotFound_FileName, _fileName), _fileName); + } + // For managed assemblies, read the file version information from the assembly's metadata. // This isn't quite what's done on Windows, which uses the Win32 GetFileVersionInfo to read // the Win32 resource information from the file, and the managed compiler uses these attributes @@ -36,20 +44,10 @@ private FileVersionInfo(string fileName) /// true if the file is a managed assembly; otherwise, false. private bool TryLoadManagedAssemblyMetadata() { - // First make sure it's a file we can actually read from. Only regular files are relevant, - // and attempting to open and read from a file such as a named pipe file could cause us to - // stop responding (waiting for someone else to open and write to the file). - Interop.Sys.FileStatus fileStatus; - if (Interop.Sys.Stat(_fileName, out fileStatus) != 0 || - (fileStatus.Mode & Interop.Sys.FileTypes.S_IFMT) != Interop.Sys.FileTypes.S_IFREG) - { - throw new FileNotFoundException(SR.Format(SR.IO_FileNotFound_FileName, _fileName), _fileName); - } - try { // Try to load the file using the managed metadata reader - using (FileStream assemblyStream = new FileStream(_fileName, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 0x1000, useAsync: false)) + using (FileStream assemblyStream = File.OpenRead(_fileName)) using (PEReader peReader = new PEReader(assemblyStream)) { if (peReader.HasMetadata) @@ -63,7 +61,14 @@ private bool TryLoadManagedAssemblyMetadata() } } } - catch (BadImageFormatException) { } + catch + { + // Obtaining this information is best effort and should not throw. + // Possible exceptions include BadImageFormatException if the file isn't an assembly, + // UnauthorizedAccessException if the caller doesn't have permissions to read the file, + // and other potential exceptions thrown by the FileStream ctor. + } + return false; } diff --git a/src/libraries/System.Diagnostics.FileVersionInfo/tests/System.Diagnostics.FileVersionInfo.Tests/AssemblyInfo.cs b/src/libraries/System.Diagnostics.FileVersionInfo/tests/System.Diagnostics.FileVersionInfo.Tests/AssemblyInfo.cs index 85b21b953764df..d96dcc30ef2bbe 100644 --- a/src/libraries/System.Diagnostics.FileVersionInfo/tests/System.Diagnostics.FileVersionInfo.Tests/AssemblyInfo.cs +++ b/src/libraries/System.Diagnostics.FileVersionInfo/tests/System.Diagnostics.FileVersionInfo.Tests/AssemblyInfo.cs @@ -3,4 +3,4 @@ using Xunit; -[assembly: SkipOnMono("System.Diagnostics.FileVersionInfo is not supported on wasm.", TestPlatforms.Browser)] +[assembly: SkipOnPlatform(TestPlatforms.Browser, "System.Diagnostics.FileVersionInfo is not supported on Browser.")] diff --git a/src/libraries/System.Diagnostics.Process/tests/AssemblyInfo.cs b/src/libraries/System.Diagnostics.Process/tests/AssemblyInfo.cs index ae8def55d925a3..80471364814581 100644 --- a/src/libraries/System.Diagnostics.Process/tests/AssemblyInfo.cs +++ b/src/libraries/System.Diagnostics.Process/tests/AssemblyInfo.cs @@ -7,4 +7,4 @@ // like the console code page and environment variables [assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly)] -[assembly: SkipOnMono("System.Diagnostics.Process is not supported on wasm.", TestPlatforms.Browser)] +[assembly: SkipOnPlatform(TestPlatforms.Browser, "System.Diagnostics.Process is not supported on Browser.")] diff --git a/src/libraries/System.Diagnostics.Process/tests/ProcessStreamReadTests.cs b/src/libraries/System.Diagnostics.Process/tests/ProcessStreamReadTests.cs index 1f1027a58573ff..2620f965411e0c 100644 --- a/src/libraries/System.Diagnostics.Process/tests/ProcessStreamReadTests.cs +++ b/src/libraries/System.Diagnostics.Process/tests/ProcessStreamReadTests.cs @@ -345,7 +345,7 @@ async private Task WaitPipeSignal(PipeStream pipe, int millisecond) } } - [PlatformSpecific(~TestPlatforms.Windows)] // currently on Windows these operations async-over-sync on Windows + [SkipOnPlatform(TestPlatforms.Windows, "currently on Windows these operations async-over-sync on Windows")] [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] public async Task ReadAsync_OutputStreams_Cancel_RespondsQuickly() { diff --git a/src/libraries/System.Diagnostics.Process/tests/ProcessTests.Unix.cs b/src/libraries/System.Diagnostics.Process/tests/ProcessTests.Unix.cs index e64153fe1e1a0b..dccedb7e01dc5d 100644 --- a/src/libraries/System.Diagnostics.Process/tests/ProcessTests.Unix.cs +++ b/src/libraries/System.Diagnostics.Process/tests/ProcessTests.Unix.cs @@ -157,7 +157,7 @@ public void ProcessStart_UseShellExecute_OnUnix_SuccessWhenProgramInstalled(bool } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // On OSX, ProcessName returns the script interpreter. + [SkipOnPlatform(TestPlatforms.OSX, "On OSX, ProcessName returns the script interpreter.")] public void ProcessNameMatchesScriptName() { string scriptName = GetTestFileName(); diff --git a/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs b/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs index 2e206aa77d2545..8ece605da6f1cc 100644 --- a/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs +++ b/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs @@ -190,7 +190,7 @@ public void ProcessStart_TryOpenFolder_UseShellExecuteIsFalse_ThrowsWin32Excepti } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // OSX doesn't support throwing on Process.Start + [SkipOnPlatform(TestPlatforms.OSX, "OSX doesn't support throwing on Process.Start")] public void TestStartWithBadWorkingDirectory() { string program; @@ -542,7 +542,7 @@ public void TestMaxWorkingSet() } [Fact] - [PlatformSpecific(~(TestPlatforms.OSX | TestPlatforms.FreeBSD))] // Getting MaxWorkingSet is not supported on OSX and BSD. + [SkipOnPlatform(TestPlatforms.OSX | TestPlatforms.FreeBSD, "Getting MaxWorkingSet is not supported on OSX and BSD.")] public void MaxWorkingSet_GetNotStarted_ThrowsInvalidOperationException() { var process = new Process(); @@ -597,7 +597,7 @@ public void TestMinWorkingSet() } [Fact] - [PlatformSpecific(~(TestPlatforms.OSX | TestPlatforms.FreeBSD))] // Getting MinWorkingSet is not supported on OSX and BSD. + [SkipOnPlatform(TestPlatforms.OSX | TestPlatforms.FreeBSD, "Getting MinWorkingSet is not supported on OSX and BSD.")] public void MinWorkingSet_GetNotStarted_ThrowsInvalidOperationException() { var process = new Process(); @@ -896,7 +896,7 @@ public void ExitTime_GetNotStarted_ThrowsInvalidOperationException() } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - [PlatformSpecific(~(TestPlatforms.OSX | TestPlatforms.FreeBSD))] // getting/setting affinity not supported on OSX and BSD + [SkipOnPlatform(TestPlatforms.OSX | TestPlatforms.FreeBSD, "getting/setting affinity not supported on OSX and BSD")] public void TestProcessorAffinity() { CreateDefaultProcess(); diff --git a/src/libraries/System.Security.Cryptography.Csp/tests/AssemblyInfo.cs b/src/libraries/System.Diagnostics.StackTrace/tests/ExceptionTestAssembly/ExceptionTestAssembly.cs similarity index 58% rename from src/libraries/System.Security.Cryptography.Csp/tests/AssemblyInfo.cs rename to src/libraries/System.Diagnostics.StackTrace/tests/ExceptionTestAssembly/ExceptionTestAssembly.cs index 3a74f33d7aa32e..49902b1000c024 100644 --- a/src/libraries/System.Security.Cryptography.Csp/tests/AssemblyInfo.cs +++ b/src/libraries/System.Diagnostics.StackTrace/tests/ExceptionTestAssembly/ExceptionTestAssembly.cs @@ -2,6 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using Xunit; -[assembly: ActiveIssue("https://github.com/dotnet/runtime/issues/37669", TestPlatforms.Browser)] +class Program +{ + public static void Foo() + { + throw new NullReferenceException(); + } +} diff --git a/src/libraries/System.Diagnostics.StackTrace/tests/ExceptionTestAssembly/ExceptionTestAssembly.csproj b/src/libraries/System.Diagnostics.StackTrace/tests/ExceptionTestAssembly/ExceptionTestAssembly.csproj new file mode 100644 index 00000000000000..d7b60a977d7ced --- /dev/null +++ b/src/libraries/System.Diagnostics.StackTrace/tests/ExceptionTestAssembly/ExceptionTestAssembly.csproj @@ -0,0 +1,10 @@ + + + true + netstandard2.0 + None + + + + + diff --git a/src/libraries/System.Diagnostics.StackTrace/tests/StackTraceTests.cs b/src/libraries/System.Diagnostics.StackTrace/tests/StackTraceTests.cs index 894374ba29b4dc..0464a22a44014c 100644 --- a/src/libraries/System.Diagnostics.StackTrace/tests/StackTraceTests.cs +++ b/src/libraries/System.Diagnostics.StackTrace/tests/StackTraceTests.cs @@ -3,9 +3,13 @@ using System.Collections.Generic; using System.Globalization; +using System.IO; using System.Linq; using System.Reflection; +using System.Reflection.Emit; using System.Runtime.CompilerServices; +using System.Text.RegularExpressions; +using Microsoft.DotNet.RemoteExecutor; using Xunit; namespace System.Diagnostics @@ -300,6 +304,72 @@ public void ToString_NullFrame_ThrowsNullReferenceException() Assert.Equal(Environment.NewLine, stackTrace.ToString()); } + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/51096", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoInterpreter))] + public void ToString_ShowILOffset() + { + string AssemblyName = "ExceptionTestAssembly.dll"; + string SourceTestAssemblyPath = Path.Combine(Environment.CurrentDirectory, AssemblyName); + string regPattern = @":token 0x([a-f0-9]*)\+0x([a-f0-9]*)"; + + // Normal loading case + RemoteExecutor.Invoke((asmPath, asmName, p) => + { + AppContext.SetSwitch("Switch.System.Diagnostics.StackTrace.ShowILOffsets", true); + var asm = Assembly.LoadFrom(asmPath); + try + { + asm.GetType("Program").GetMethod("Foo").Invoke(null, null); + } + catch (Exception e) + { + Assert.Contains(asmName, e.InnerException.StackTrace); + Assert.True(Regex.Match(e.InnerException.StackTrace, p).Success); + } + }, SourceTestAssemblyPath, AssemblyName, regPattern).Dispose(); + + // Assembly.Load(Byte[]) case + RemoteExecutor.Invoke((asmPath, asmName, p) => + { + AppContext.SetSwitch("Switch.System.Diagnostics.StackTrace.ShowILOffsets", true); + var inMemBlob = File.ReadAllBytes(asmPath); + var asm2 = Assembly.Load(inMemBlob); + try + { + asm2.GetType("Program").GetMethod("Foo").Invoke(null, null); + } + catch (Exception e) + { + Assert.Contains(asmName, e.InnerException.StackTrace); + Assert.True(Regex.Match(e.InnerException.StackTrace, p).Success); + } + }, SourceTestAssemblyPath, AssemblyName, regPattern).Dispose(); + + // AssmblyBuilder.DefineDynamicAssembly() case + RemoteExecutor.Invoke((p) => + { + AppContext.SetSwitch("Switch.System.Diagnostics.StackTrace.ShowILOffsets", true); + AssemblyName asmName = new AssemblyName("ExceptionTestAssembly"); + AssemblyBuilder asmBldr = AssemblyBuilder.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.Run); + ModuleBuilder modBldr = asmBldr.DefineDynamicModule(asmName.Name); + TypeBuilder tBldr = modBldr.DefineType("Program"); + MethodBuilder mBldr = tBldr.DefineMethod("Foo", MethodAttributes.Public | MethodAttributes.Static, null, null); + ILGenerator ilGen = mBldr.GetILGenerator(); + ilGen.ThrowException(typeof(NullReferenceException)); + ilGen.Emit(OpCodes.Ret); + Type t = tBldr.CreateType(); + try + { + t.InvokeMember("Foo", BindingFlags.InvokeMethod, null, null, null); + } + catch (Exception e) + { + Assert.Contains("RefEmit_InMemoryManifestModule", e.InnerException.StackTrace); + Assert.True(Regex.Match(e.InnerException.StackTrace, p).Success); + } + }, regPattern).Dispose(); + } + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] private static StackTrace NoParameters() => new StackTrace(); [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] diff --git a/src/libraries/System.Diagnostics.StackTrace/tests/System.Diagnostics.StackTrace.Tests.csproj b/src/libraries/System.Diagnostics.StackTrace/tests/System.Diagnostics.StackTrace.Tests.csproj index b112648c183634..93673781e705f4 100644 --- a/src/libraries/System.Diagnostics.StackTrace/tests/System.Diagnostics.StackTrace.Tests.csproj +++ b/src/libraries/System.Diagnostics.StackTrace/tests/System.Diagnostics.StackTrace.Tests.csproj @@ -2,6 +2,7 @@ $(NetCoreAppCurrent) true + true @@ -17,4 +18,7 @@ - \ No newline at end of file + + + + diff --git a/src/libraries/System.Drawing.Common/tests/AssemblyInfo.cs b/src/libraries/System.Drawing.Common/tests/AssemblyInfo.cs index 17f46496f4fcef..b1abf9bdf2391f 100644 --- a/src/libraries/System.Drawing.Common/tests/AssemblyInfo.cs +++ b/src/libraries/System.Drawing.Common/tests/AssemblyInfo.cs @@ -5,4 +5,4 @@ using Xunit; [assembly: ActiveIssue("https://github.com/dotnet/runtime/issues/35917", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoInterpreter))] -[assembly: SkipOnMono("System.Drawing.Common is not supported on Browser", TestPlatforms.Browser)] +[assembly: SkipOnPlatform(TestPlatforms.Browser, "System.Drawing.Common is not supported on Browser")] diff --git a/src/libraries/System.Drawing.Common/tests/System.Drawing.Common.Tests.csproj b/src/libraries/System.Drawing.Common/tests/System.Drawing.Common.Tests.csproj index 9dfd74f0454a21..9d0c15dec7797b 100644 --- a/src/libraries/System.Drawing.Common/tests/System.Drawing.Common.Tests.csproj +++ b/src/libraries/System.Drawing.Common/tests/System.Drawing.Common.Tests.csproj @@ -3,6 +3,7 @@ true true $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;net48 + true diff --git a/src/libraries/System.IO.Compression.Brotli/tests/AssemblyInfo.cs b/src/libraries/System.IO.Compression.Brotli/tests/AssemblyInfo.cs index 1038dc251cb5b3..7f8b8095c28136 100644 --- a/src/libraries/System.IO.Compression.Brotli/tests/AssemblyInfo.cs +++ b/src/libraries/System.IO.Compression.Brotli/tests/AssemblyInfo.cs @@ -4,4 +4,4 @@ using System; using Xunit; -[assembly: SkipOnMono("System.IO.Compression.Brotli is not supported on Browser", TestPlatforms.Browser)] +[assembly: SkipOnPlatform(TestPlatforms.Browser, "System.IO.Compression.Brotli is not supported on Browser")] diff --git a/src/libraries/System.IO.Compression.Brotli/tests/System.IO.Compression.Brotli.Tests.csproj b/src/libraries/System.IO.Compression.Brotli/tests/System.IO.Compression.Brotli.Tests.csproj index 60afa44f33d27f..d68a7be37b621e 100644 --- a/src/libraries/System.IO.Compression.Brotli/tests/System.IO.Compression.Brotli.Tests.csproj +++ b/src/libraries/System.IO.Compression.Brotli/tests/System.IO.Compression.Brotli.Tests.csproj @@ -2,6 +2,7 @@ $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Browser true + true diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/AssemblyInfo.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/AssemblyInfo.cs index e5921a9bc9b3f9..94b42c5041cc9f 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/AssemblyInfo.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/AssemblyInfo.cs @@ -4,4 +4,4 @@ using System; using Xunit; -[assembly: SkipOnMono("System.IO.FileSystem.Watcher is not supported on wasm", TestPlatforms.Browser)] +[assembly: SkipOnPlatform(TestPlatforms.Browser, "System.IO.FileSystem.Watcher is not supported on Browser")] diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Move.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Move.cs index 2f2230c7b89d79..b40fac4e7602f5 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Move.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Move.cs @@ -39,7 +39,7 @@ public void Directory_Move_Multiple_From_Watched_To_Unwatched_Mac(int filesCount } [Theory] - [PlatformSpecific(~TestPlatforms.OSX)] + [SkipOnPlatform(TestPlatforms.OSX, "Not supported on OSX.")] [InlineData(1)] [InlineData(2)] [InlineData(3)] @@ -49,7 +49,7 @@ public void Directory_Move_Multiple_From_Watched_To_Unwatched(int filesCount) } [Theory] - [PlatformSpecific(~TestPlatforms.FreeBSD)] + [SkipOnPlatform(TestPlatforms.FreeBSD, "Not supported on FreeBSD.")] [InlineData(1)] [InlineData(2)] [InlineData(3)] diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Move.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Move.cs index 2af77eb827a273..05ba2620929768 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Move.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Move.cs @@ -43,7 +43,7 @@ public void File_Move_Multiple_From_Watched_To_Unwatched_Mac(int filesCount) } [Theory] - [PlatformSpecific(~TestPlatforms.OSX)] + [SkipOnPlatform(TestPlatforms.OSX, "Not supported on OSX.")] [InlineData(1)] [InlineData(2)] [InlineData(3)] diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/System.IO.FileSystem.Watcher.Tests.csproj b/src/libraries/System.IO.FileSystem.Watcher/tests/System.IO.FileSystem.Watcher.Tests.csproj index 6198325a2dfb7a..4c7b2edca0e061 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/System.IO.FileSystem.Watcher.Tests.csproj +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/System.IO.FileSystem.Watcher.Tests.csproj @@ -2,6 +2,7 @@ true $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Linux;$(NetCoreAppCurrent)-OSX;$(NetCoreAppCurrent)-iOS;$(NetCoreAppCurrent)-tvOS;$(NetCoreAppCurrent)-FreeBSD + true diff --git a/src/libraries/System.IO.FileSystem/tests/Directory/CreateDirectory.cs b/src/libraries/System.IO.FileSystem/tests/Directory/CreateDirectory.cs index 78c234d5627cd5..ed833bbc89212d 100644 --- a/src/libraries/System.IO.FileSystem/tests/Directory/CreateDirectory.cs +++ b/src/libraries/System.IO.FileSystem/tests/Directory/CreateDirectory.cs @@ -216,7 +216,7 @@ public void DirectoryEqualToMaxDirectory_CanBeCreatedAllAtOnce() [Theory, MemberData(nameof(PathsWithComponentLongerThanMaxComponent))] - [PlatformSpecific(~TestPlatforms.Browser)] // Browser does not have a limit on the maximum component length + [SkipOnPlatform(TestPlatforms.Browser, "Browser does not have a limit on the maximum component length")] public void DirectoryWithComponentLongerThanMaxComponentAsPath_ThrowsException(string path) { AssertExtensions.ThrowsAny(() => Create(path)); diff --git a/src/libraries/System.IO.FileSystem/tests/File/Copy.cs b/src/libraries/System.IO.FileSystem/tests/File/Copy.cs index aa3c929bad1c84..0654f99dc9ce32 100644 --- a/src/libraries/System.IO.FileSystem/tests/File/Copy.cs +++ b/src/libraries/System.IO.FileSystem/tests/File/Copy.cs @@ -343,7 +343,7 @@ public void WindowsAlternateDataStreamOverwrite(string defaultStream, string alt /// /// Single tests that shouldn't be duplicated by inheritance. /// - [PlatformSpecific(~TestPlatforms.Browser)] // https://github.com/dotnet/runtime/issues/40867 - flock not supported + [SkipOnPlatform(TestPlatforms.Browser, "https://github.com/dotnet/runtime/issues/40867 - flock not supported")] public sealed class File_Copy_Single : FileSystemTest { [Fact] diff --git a/src/libraries/System.IO.FileSystem/tests/File/Create.cs b/src/libraries/System.IO.FileSystem/tests/File/Create.cs index 447bb323baffd0..d89fcd26a5b9c2 100644 --- a/src/libraries/System.IO.FileSystem/tests/File/Create.cs +++ b/src/libraries/System.IO.FileSystem/tests/File/Create.cs @@ -150,7 +150,7 @@ public void OverwriteReadOnly() #region PlatformSpecific [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] // Browser platform volume does not limit segments + [SkipOnPlatform(TestPlatforms.Browser, "Browser platform volume does not limit segments")] public void LongPathSegment() { DirectoryInfo testDir = Directory.CreateDirectory(GetTestFilePath()); diff --git a/src/libraries/System.IO.FileSystem/tests/FileInfo/Exists.cs b/src/libraries/System.IO.FileSystem/tests/FileInfo/Exists.cs index b0f1779745f613..72b8ba48ba3ab2 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileInfo/Exists.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileInfo/Exists.cs @@ -146,7 +146,7 @@ public void UnsharedFileExists() } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - [PlatformSpecific(~TestPlatforms.OSX)] + [SkipOnPlatform(TestPlatforms.OSX, "Not supported on OSX.")] public void LockedFileExists() { string path = GetTestFilePath(); diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamConformanceTests.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamConformanceTests.cs index 82b10a882c3d38..f086c2cc23609f 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamConformanceTests.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamConformanceTests.cs @@ -161,6 +161,31 @@ public async Task LengthIsNotCachedAfterHandleHasBeenExposed(FileAccess fileAcce Assert.Equal(2, stream.Length); Assert.Equal(2, createdFromHandle.Length); } + + [Fact] + public async Task WriteByteFlushesTheBufferWhenItBecomesFull() + { + string filePath; + List writtenBytes = new List(); + + using (FileStream stream = (FileStream)await CreateWriteOnlyStreamCore(Array.Empty())) + { + filePath = stream.Name; + + stream.WriteByte(0); + writtenBytes.Add(0); + + byte[] bytes = new byte[BufferSize - 1]; + stream.Write(bytes.AsSpan()); + writtenBytes.AddRange(bytes); + + stream.WriteByte(1); + writtenBytes.Add(1); + } + + byte[] allBytes = File.ReadAllBytes(filePath); + Assert.Equal(writtenBytes.ToArray(), allBytes); + } } public class UnbufferedSyncFileStreamStandaloneConformanceTests : FileStreamStandaloneConformanceTests @@ -176,7 +201,7 @@ public class BufferedSyncFileStreamStandaloneConformanceTests : FileStreamStanda } [ActiveIssue("https://github.com/dotnet/runtime/issues/34583", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] - [PlatformSpecific(~TestPlatforms.Browser)] // copied from base class due to https://github.com/xunit/xunit/issues/2186 + [SkipOnPlatform(TestPlatforms.Browser, "lots of operations aren't supported on browser")] // copied from StreamConformanceTests base class due to https://github.com/xunit/xunit/issues/2186 public class UnbufferedAsyncFileStreamStandaloneConformanceTests : FileStreamStandaloneConformanceTests { protected override FileOptions Options => FileOptions.Asynchronous; @@ -184,7 +209,7 @@ public class UnbufferedAsyncFileStreamStandaloneConformanceTests : FileStreamSta } [ActiveIssue("https://github.com/dotnet/runtime/issues/34583", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] - [PlatformSpecific(~TestPlatforms.Browser)] // copied from base class due to https://github.com/xunit/xunit/issues/2186 + [SkipOnPlatform(TestPlatforms.Browser, "lots of operations aren't supported on browser")] // copied from StreamConformanceTests base class due to https://github.com/xunit/xunit/issues/2186 public class BufferedAsyncFileStreamStandaloneConformanceTests : FileStreamStandaloneConformanceTests { protected override FileOptions Options => FileOptions.Asynchronous; diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/Flush.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/Flush.cs index 10e522185726ee..25dd6422410aa5 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileStream/Flush.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/Flush.cs @@ -147,7 +147,7 @@ public void SafeFileHandleCallsFlush_flushToDisk_False() [InlineData(null)] [InlineData(false)] [InlineData(true)] - [PlatformSpecific(~TestPlatforms.Browser)] // IO.Pipes not supported + [SkipOnPlatform(TestPlatforms.Browser, "IO.Pipes not supported")] public void FlushCanBeUsedOnPipes_Success(bool? flushToDisk) { using (var pipeStream = new AnonymousPipeServerStream(PipeDirection.In)) diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/LockUnlock.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/LockUnlock.cs index 1da23bf4c08258..d2f7bf7ec62c15 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileStream/LockUnlock.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/LockUnlock.cs @@ -58,7 +58,7 @@ public void LockUnlock_Unsupported_OSX() [InlineData(200, 50, 150)] [InlineData(200, 100, 100)] [InlineData(20, 2000, 1000)] - [PlatformSpecific(~TestPlatforms.OSX)] + [SkipOnPlatform(TestPlatforms.OSX, "Not supported on OSX.")] public void Lock_Unlock_Successful(long fileLength, long position, long length) { string path = GetTestFilePath(); @@ -75,7 +75,7 @@ public void Lock_Unlock_Successful(long fileLength, long position, long length) [InlineData(FileAccess.Read)] [InlineData(FileAccess.Write)] [InlineData(FileAccess.ReadWrite)] - [PlatformSpecific(~TestPlatforms.OSX)] + [SkipOnPlatform(TestPlatforms.OSX, "Not supported on OSX.")] public void Lock_Unlock_Successful_AlternateFileAccess(FileAccess fileAccess) { string path = GetTestFilePath(); @@ -89,7 +89,7 @@ public void Lock_Unlock_Successful_AlternateFileAccess(FileAccess fileAccess) [Theory] [InlineData(10, 0, 2, 3, 5)] - [PlatformSpecific(~TestPlatforms.OSX)] + [SkipOnPlatform(TestPlatforms.OSX, "Not supported on OSX.")] public void NonOverlappingRegions_Success(long fileLength, long firstPosition, long firstLength, long secondPosition, long secondLength) { string path = GetTestFilePath(); @@ -176,7 +176,7 @@ public void OverlappingRegionsFromSameProcess_AllowedOnUnix(long fileLength, lon [InlineData(10, 3, 5, 2, 6)] [InlineData(10, 3, 5, 2, 4)] [InlineData(10, 3, 5, 4, 6)] - [PlatformSpecific(~TestPlatforms.OSX)] + [SkipOnPlatform(TestPlatforms.OSX, "Not supported on OSX.")] public void OverlappingRegionsFromOtherProcess_ThrowsException(long fileLength, long firstPosition, long firstLength, long secondPosition, long secondLength) { string path = GetTestFilePath(); @@ -231,7 +231,7 @@ public void OverlappingRegionsFromOtherProcess_With_ReadLock_AllowedOnLinux() [InlineData(FileAccess.Read)] [InlineData(FileAccess.Write)] [InlineData(FileAccess.ReadWrite)] - [PlatformSpecific(~TestPlatforms.OSX)] + [SkipOnPlatform(TestPlatforms.OSX, "Not supported on OSX.")] public void OverlappingRegionsFromOtherProcess_With_WriteLock_ThrowsException(FileAccess fileAccess) { string path = GetTestFilePath(); diff --git a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/Net5CompatSwitchTests.cs b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/Net5CompatSwitchTests.cs index bb2d05086cf0ad..24f9026b54dfff 100644 --- a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/Net5CompatSwitchTests.cs +++ b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/Net5CompatSwitchTests.cs @@ -20,7 +20,7 @@ public static void LegacySwitchIsHonored() .GetField("_strategy", BindingFlags.NonPublic | BindingFlags.Instance) .GetValue(fileStream); - Assert.DoesNotContain("Net5Compat", strategy.GetType().FullName); + Assert.Contains("Net5Compat", strategy.GetType().FullName); } File.Delete(filePath); diff --git a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/runtimeconfig.template.json b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/runtimeconfig.template.json index 0c1a3482aba4f8..6e843517fe47af 100644 --- a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/runtimeconfig.template.json +++ b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/runtimeconfig.template.json @@ -1,5 +1,5 @@ { "configProperties": { - "System.IO.UseNet5CompatFileStream": false + "System.IO.UseNet5CompatFileStream": true } } diff --git a/src/libraries/System.IO.IsolatedStorage/tests/AssemblyInfo.cs b/src/libraries/System.IO.IsolatedStorage/tests/AssemblyInfo.cs index dadc27e2fbf2b3..2e17f1038062cf 100644 --- a/src/libraries/System.IO.IsolatedStorage/tests/AssemblyInfo.cs +++ b/src/libraries/System.IO.IsolatedStorage/tests/AssemblyInfo.cs @@ -9,4 +9,4 @@ // it's own store. [assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly, DisableTestParallelization = true)] [assembly: ActiveIssue("https://github.com/dotnet/runtime/issues/48720", TestPlatforms.AnyUnix, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] -[assembly: SkipOnMono("System.IO.IsolatedStorage is not supported on Browser", TestPlatforms.Browser)] +[assembly: SkipOnPlatform(TestPlatforms.Browser, "System.IO.IsolatedStorage is not supported on Browser")] diff --git a/src/libraries/System.IO.IsolatedStorage/tests/System.IO.IsolatedStorage.Tests.csproj b/src/libraries/System.IO.IsolatedStorage/tests/System.IO.IsolatedStorage.Tests.csproj index b37dff5c1d8974..5765f53e6056ec 100644 --- a/src/libraries/System.IO.IsolatedStorage/tests/System.IO.IsolatedStorage.Tests.csproj +++ b/src/libraries/System.IO.IsolatedStorage/tests/System.IO.IsolatedStorage.Tests.csproj @@ -1,6 +1,7 @@ $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Browser + true diff --git a/src/libraries/System.IO.MemoryMappedFiles/tests/MemoryMappedFile.CreateFromFile.Tests.cs b/src/libraries/System.IO.MemoryMappedFiles/tests/MemoryMappedFile.CreateFromFile.Tests.cs index 4a08c3f7e3a80c..1df45e91bf5522 100644 --- a/src/libraries/System.IO.MemoryMappedFiles/tests/MemoryMappedFile.CreateFromFile.Tests.cs +++ b/src/libraries/System.IO.MemoryMappedFiles/tests/MemoryMappedFile.CreateFromFile.Tests.cs @@ -613,7 +613,7 @@ public void FileInUse_CreateFromFile_FailsWithExistingReadWriteMap() /// Test exceptional behavior when trying to create a map for a non-shared file that's currently in use. /// [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] // the emscripten implementation ignores FileShare.None + [SkipOnPlatform(TestPlatforms.Browser, "the emscripten implementation ignores FileShare.None")] public void FileInUse_CreateFromFile_FailsWithExistingNoShareFile() { // Already opened with a FileStream @@ -768,7 +768,7 @@ public void LeaveOpenRespected_OutstandingViews(bool leaveOpen) /// Test to validate we can create multiple maps from the same FileStream. /// [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] // the emscripten implementation doesn't share data + [SkipOnPlatform(TestPlatforms.Browser, "the emscripten implementation doesn't share data")] public void MultipleMapsForTheSameFileStream() { const int Capacity = 4096; @@ -829,7 +829,7 @@ public void FileSizeExpandsToCapacity() /// /// Test the exceptional behavior when attempting to create a map so large it's not supported. /// - [PlatformSpecific(~TestPlatforms.OSX)] // Because of the file-based backing, OS X pops up a warning dialog about being out-of-space (even though we clean up immediately) + [SkipOnPlatform(TestPlatforms.OSX, "Because of the file-based backing, OS X pops up a warning dialog about being out-of-space (even though we clean up immediately)")] [Fact] public void TooLargeCapacity() { diff --git a/src/libraries/System.IO.MemoryMappedFiles/tests/MemoryMappedViewAccessor.Tests.cs b/src/libraries/System.IO.MemoryMappedFiles/tests/MemoryMappedViewAccessor.Tests.cs index 0e0f76284dfeb8..3193942d2fafd8 100644 --- a/src/libraries/System.IO.MemoryMappedFiles/tests/MemoryMappedViewAccessor.Tests.cs +++ b/src/libraries/System.IO.MemoryMappedFiles/tests/MemoryMappedViewAccessor.Tests.cs @@ -347,7 +347,7 @@ public void FlushSupportedOnBothReadAndWriteAccessors(MemoryMappedFileAccess acc /// Test to validate that multiple accessors over the same map share data appropriately. /// [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] // the emscripten implementation doesn't share data + [SkipOnPlatform(TestPlatforms.Browser, "the emscripten implementation doesn't share data")] public void ViewsShareData() { const int MapLength = 256; diff --git a/src/libraries/System.IO.MemoryMappedFiles/tests/MemoryMappedViewStream.Tests.cs b/src/libraries/System.IO.MemoryMappedFiles/tests/MemoryMappedViewStream.Tests.cs index f8c073d86a269a..b1b4b1a610ee5a 100644 --- a/src/libraries/System.IO.MemoryMappedFiles/tests/MemoryMappedViewStream.Tests.cs +++ b/src/libraries/System.IO.MemoryMappedFiles/tests/MemoryMappedViewStream.Tests.cs @@ -221,7 +221,7 @@ public void AllReadWriteMethods(long offset, long size) /// Test to validate that multiple accessors over the same map share data appropriately. /// [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] // the emscripten implementation doesn't share data + [SkipOnPlatform(TestPlatforms.Browser, "the emscripten implementation doesn't share data")] public void ViewsShareData() { const int MapLength = 256; diff --git a/src/libraries/System.IO.Pipelines/tests/PipeWriterTests.cs b/src/libraries/System.IO.Pipelines/tests/PipeWriterTests.cs index e6a64d47f28be6..0abdfa252da78d 100644 --- a/src/libraries/System.IO.Pipelines/tests/PipeWriterTests.cs +++ b/src/libraries/System.IO.Pipelines/tests/PipeWriterTests.cs @@ -229,7 +229,7 @@ public async Task WritesUsingGetMemoryWorks() } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] // allocates too much memory + [SkipOnPlatform(TestPlatforms.Browser, "allocates too much memory")] public async Task CompleteWithLargeWriteThrows() { var pipe = new Pipe(); diff --git a/src/libraries/System.IO.Pipes/tests/AssemblyInfo.cs b/src/libraries/System.IO.Pipes/tests/AssemblyInfo.cs index 394d602401c8c2..806ccf216126e3 100644 --- a/src/libraries/System.IO.Pipes/tests/AssemblyInfo.cs +++ b/src/libraries/System.IO.Pipes/tests/AssemblyInfo.cs @@ -4,4 +4,4 @@ using Xunit; [assembly: ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] -[assembly: SkipOnMono("System.IO.Pipes is not supported on Browser", TestPlatforms.Browser)] +[assembly: SkipOnPlatform(TestPlatforms.Browser, "System.IO.Pipes is not supported on Browser")] diff --git a/src/libraries/System.IO/tests/BinaryWriter/BinaryWriter.EncodingTests.cs b/src/libraries/System.IO/tests/BinaryWriter/BinaryWriter.EncodingTests.cs index b22020a36dc4f2..6a41e67c0e8086 100644 --- a/src/libraries/System.IO/tests/BinaryWriter/BinaryWriter.EncodingTests.cs +++ b/src/libraries/System.IO/tests/BinaryWriter/BinaryWriter.EncodingTests.cs @@ -188,7 +188,7 @@ public void WriteString_NotUtf8(int stringLengthInChars) } [Fact] - [PlatformSpecific(~TestPlatforms.Android)] // OOM on Android could be uncatchable & kill the test runner + [SkipOnPlatform(TestPlatforms.Android, "OOM on Android could be uncatchable & kill the test runner")] public unsafe void WriteChars_VeryLargeArray_DoesNotOverflow() { const nuint INPUT_LEN_IN_CHARS = 1_500_000_000; diff --git a/src/libraries/System.IO/tests/Net5CompatTests/runtimeconfig.template.json b/src/libraries/System.IO/tests/Net5CompatTests/runtimeconfig.template.json index 0c1a3482aba4f8..6e843517fe47af 100644 --- a/src/libraries/System.IO/tests/Net5CompatTests/runtimeconfig.template.json +++ b/src/libraries/System.IO/tests/Net5CompatTests/runtimeconfig.template.json @@ -1,5 +1,5 @@ { "configProperties": { - "System.IO.UseNet5CompatFileStream": false + "System.IO.UseNet5CompatFileStream": true } } diff --git a/src/libraries/System.IO/tests/StreamReader/StreamReaderTests.cs b/src/libraries/System.IO/tests/StreamReader/StreamReaderTests.cs index a16321c03f087e..4e4bb14d233e4a 100644 --- a/src/libraries/System.IO/tests/StreamReader/StreamReaderTests.cs +++ b/src/libraries/System.IO/tests/StreamReader/StreamReaderTests.cs @@ -512,7 +512,7 @@ public async Task ReadBlockAsync_RepeatsReadsUntilReadDesiredAmount() [InlineData(0, true)] [InlineData(1, false)] [InlineData(1, true)] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public async Task ReadAsync_Canceled_ThrowsException(int method, bool precanceled) { Func> func = method switch diff --git a/src/libraries/System.Linq.Queryable/ref/System.Linq.Queryable.cs b/src/libraries/System.Linq.Queryable/ref/System.Linq.Queryable.cs index bd3497d53c6580..a6ae3b48620a9c 100644 --- a/src/libraries/System.Linq.Queryable/ref/System.Linq.Queryable.cs +++ b/src/libraries/System.Linq.Queryable/ref/System.Linq.Queryable.cs @@ -77,18 +77,22 @@ public static partial class Queryable public static int Count(this System.Linq.IQueryable source, System.Linq.Expressions.Expression> predicate) { throw null; } public static System.Linq.IQueryable DefaultIfEmpty(this System.Linq.IQueryable source) { throw null; } public static System.Linq.IQueryable DefaultIfEmpty(this System.Linq.IQueryable source, TSource defaultValue) { throw null; } + public static System.Linq.IQueryable DistinctBy(this System.Linq.IQueryable source, System.Linq.Expressions.Expression> keySelector) { throw null; } + public static System.Linq.IQueryable DistinctBy(this System.Linq.IQueryable source, System.Linq.Expressions.Expression> keySelector, System.Collections.Generic.IEqualityComparer? comparer) { throw null; } public static System.Linq.IQueryable Distinct(this System.Linq.IQueryable source) { throw null; } public static System.Linq.IQueryable Distinct(this System.Linq.IQueryable source, System.Collections.Generic.IEqualityComparer? comparer) { throw null; } - public static TSource? ElementAtOrDefault(this System.Linq.IQueryable source, int index) { throw null; } public static TSource? ElementAtOrDefault(this System.Linq.IQueryable source, System.Index index) { throw null; } - public static TSource ElementAt(this System.Linq.IQueryable source, int index) { throw null; } + public static TSource? ElementAtOrDefault(this System.Linq.IQueryable source, int index) { throw null; } public static TSource ElementAt(this System.Linq.IQueryable source, System.Index index) { throw null; } + public static TSource ElementAt(this System.Linq.IQueryable source, int index) { throw null; } + public static System.Linq.IQueryable ExceptBy(this System.Linq.IQueryable source1, System.Collections.Generic.IEnumerable source2, System.Linq.Expressions.Expression> keySelector) { throw null; } + public static System.Linq.IQueryable ExceptBy(this System.Linq.IQueryable source1, System.Collections.Generic.IEnumerable source2, System.Linq.Expressions.Expression> keySelector, System.Collections.Generic.IEqualityComparer? comparer) { throw null; } public static System.Linq.IQueryable Except(this System.Linq.IQueryable source1, System.Collections.Generic.IEnumerable source2) { throw null; } public static System.Linq.IQueryable Except(this System.Linq.IQueryable source1, System.Collections.Generic.IEnumerable source2, System.Collections.Generic.IEqualityComparer? comparer) { throw null; } public static TSource? FirstOrDefault(this System.Linq.IQueryable source) { throw null; } - public static TSource FirstOrDefault(this System.Linq.IQueryable source, TSource defaultValue) { throw null; } public static TSource? FirstOrDefault(this System.Linq.IQueryable source, System.Linq.Expressions.Expression> predicate) { throw null; } public static TSource FirstOrDefault(this System.Linq.IQueryable source, System.Linq.Expressions.Expression> predicate, TSource defaultValue) { throw null; } + public static TSource FirstOrDefault(this System.Linq.IQueryable source, TSource defaultValue) { throw null; } public static TSource First(this System.Linq.IQueryable source) { throw null; } public static TSource First(this System.Linq.IQueryable source, System.Linq.Expressions.Expression> predicate) { throw null; } public static System.Linq.IQueryable> GroupBy(this System.Linq.IQueryable source, System.Linq.Expressions.Expression> keySelector) { throw null; } @@ -101,21 +105,29 @@ public static partial class Queryable public static System.Linq.IQueryable GroupBy(this System.Linq.IQueryable source, System.Linq.Expressions.Expression> keySelector, System.Linq.Expressions.Expression> elementSelector, System.Linq.Expressions.Expression, TResult>> resultSelector, System.Collections.Generic.IEqualityComparer? comparer) { throw null; } public static System.Linq.IQueryable GroupJoin(this System.Linq.IQueryable outer, System.Collections.Generic.IEnumerable inner, System.Linq.Expressions.Expression> outerKeySelector, System.Linq.Expressions.Expression> innerKeySelector, System.Linq.Expressions.Expression, TResult>> resultSelector) { throw null; } public static System.Linq.IQueryable GroupJoin(this System.Linq.IQueryable outer, System.Collections.Generic.IEnumerable inner, System.Linq.Expressions.Expression> outerKeySelector, System.Linq.Expressions.Expression> innerKeySelector, System.Linq.Expressions.Expression, TResult>> resultSelector, System.Collections.Generic.IEqualityComparer? comparer) { throw null; } + public static System.Linq.IQueryable IntersectBy(this System.Linq.IQueryable source1, System.Collections.Generic.IEnumerable source2, System.Linq.Expressions.Expression> keySelector) { throw null; } + public static System.Linq.IQueryable IntersectBy(this System.Linq.IQueryable source1, System.Collections.Generic.IEnumerable source2, System.Linq.Expressions.Expression> keySelector, System.Collections.Generic.IEqualityComparer? comparer) { throw null; } public static System.Linq.IQueryable Intersect(this System.Linq.IQueryable source1, System.Collections.Generic.IEnumerable source2) { throw null; } public static System.Linq.IQueryable Intersect(this System.Linq.IQueryable source1, System.Collections.Generic.IEnumerable source2, System.Collections.Generic.IEqualityComparer? comparer) { throw null; } public static System.Linq.IQueryable Join(this System.Linq.IQueryable outer, System.Collections.Generic.IEnumerable inner, System.Linq.Expressions.Expression> outerKeySelector, System.Linq.Expressions.Expression> innerKeySelector, System.Linq.Expressions.Expression> resultSelector) { throw null; } public static System.Linq.IQueryable Join(this System.Linq.IQueryable outer, System.Collections.Generic.IEnumerable inner, System.Linq.Expressions.Expression> outerKeySelector, System.Linq.Expressions.Expression> innerKeySelector, System.Linq.Expressions.Expression> resultSelector, System.Collections.Generic.IEqualityComparer? comparer) { throw null; } public static TSource? LastOrDefault(this System.Linq.IQueryable source) { throw null; } - public static TSource LastOrDefault(this System.Linq.IQueryable source, TSource defaultValue) { throw null; } public static TSource? LastOrDefault(this System.Linq.IQueryable source, System.Linq.Expressions.Expression> predicate) { throw null; } public static TSource LastOrDefault(this System.Linq.IQueryable source, System.Linq.Expressions.Expression> predicate, TSource defaultValue) { throw null; } + public static TSource LastOrDefault(this System.Linq.IQueryable source, TSource defaultValue) { throw null; } public static TSource Last(this System.Linq.IQueryable source) { throw null; } public static TSource Last(this System.Linq.IQueryable source, System.Linq.Expressions.Expression> predicate) { throw null; } public static long LongCount(this System.Linq.IQueryable source) { throw null; } public static long LongCount(this System.Linq.IQueryable source, System.Linq.Expressions.Expression> predicate) { throw null; } + public static TSource? MaxBy(this System.Linq.IQueryable source, System.Linq.Expressions.Expression> keySelector) { throw null; } + public static TSource? MaxBy(this System.Linq.IQueryable source, System.Linq.Expressions.Expression> keySelector, System.Collections.Generic.IComparer? comparer) { throw null; } public static TSource? Max(this System.Linq.IQueryable source) { throw null; } + public static TSource? Max(this System.Linq.IQueryable source, System.Collections.Generic.IComparer? comparer) { throw null; } public static TResult? Max(this System.Linq.IQueryable source, System.Linq.Expressions.Expression> selector) { throw null; } + public static TSource? MinBy(this System.Linq.IQueryable source, System.Linq.Expressions.Expression> keySelector) { throw null; } + public static TSource? MinBy(this System.Linq.IQueryable source, System.Linq.Expressions.Expression> keySelector, System.Collections.Generic.IComparer? comparer) { throw null; } public static TSource? Min(this System.Linq.IQueryable source) { throw null; } + public static TSource? Min(this System.Linq.IQueryable source, System.Collections.Generic.IComparer? comparer) { throw null; } public static TResult? Min(this System.Linq.IQueryable source, System.Linq.Expressions.Expression> selector) { throw null; } public static System.Linq.IQueryable OfType(this System.Linq.IQueryable source) { throw null; } public static System.Linq.IOrderedQueryable OrderByDescending(this System.Linq.IQueryable source, System.Linq.Expressions.Expression> keySelector) { throw null; } @@ -133,9 +145,9 @@ public static partial class Queryable public static bool SequenceEqual(this System.Linq.IQueryable source1, System.Collections.Generic.IEnumerable source2) { throw null; } public static bool SequenceEqual(this System.Linq.IQueryable source1, System.Collections.Generic.IEnumerable source2, System.Collections.Generic.IEqualityComparer? comparer) { throw null; } public static TSource? SingleOrDefault(this System.Linq.IQueryable source) { throw null; } - public static TSource SingleOrDefault(this System.Linq.IQueryable source, TSource defaultValue) { throw null; } public static TSource? SingleOrDefault(this System.Linq.IQueryable source, System.Linq.Expressions.Expression> predicate) { throw null; } public static TSource SingleOrDefault(this System.Linq.IQueryable source, System.Linq.Expressions.Expression> predicate, TSource defaultValue) { throw null; } + public static TSource SingleOrDefault(this System.Linq.IQueryable source, TSource defaultValue) { throw null; } public static TSource Single(this System.Linq.IQueryable source) { throw null; } public static TSource Single(this System.Linq.IQueryable source, System.Linq.Expressions.Expression> predicate) { throw null; } public static System.Linq.IQueryable SkipLast(this System.Linq.IQueryable source, int count) { throw null; } @@ -171,6 +183,8 @@ public static partial class Queryable public static System.Linq.IOrderedQueryable ThenByDescending(this System.Linq.IOrderedQueryable source, System.Linq.Expressions.Expression> keySelector, System.Collections.Generic.IComparer? comparer) { throw null; } public static System.Linq.IOrderedQueryable ThenBy(this System.Linq.IOrderedQueryable source, System.Linq.Expressions.Expression> keySelector) { throw null; } public static System.Linq.IOrderedQueryable ThenBy(this System.Linq.IOrderedQueryable source, System.Linq.Expressions.Expression> keySelector, System.Collections.Generic.IComparer? comparer) { throw null; } + public static System.Linq.IQueryable UnionBy(this System.Linq.IQueryable source1, System.Collections.Generic.IEnumerable source2, System.Linq.Expressions.Expression> keySelector) { throw null; } + public static System.Linq.IQueryable UnionBy(this System.Linq.IQueryable source1, System.Collections.Generic.IEnumerable source2, System.Linq.Expressions.Expression> keySelector, System.Collections.Generic.IEqualityComparer? comparer) { throw null; } public static System.Linq.IQueryable Union(this System.Linq.IQueryable source1, System.Collections.Generic.IEnumerable source2) { throw null; } public static System.Linq.IQueryable Union(this System.Linq.IQueryable source1, System.Collections.Generic.IEnumerable source2, System.Collections.Generic.IEqualityComparer? comparer) { throw null; } public static System.Linq.IQueryable Where(this System.Linq.IQueryable source, System.Linq.Expressions.Expression> predicate) { throw null; } diff --git a/src/libraries/System.Linq.Queryable/src/System/Linq/CachedReflection.cs b/src/libraries/System.Linq.Queryable/src/System/Linq/CachedReflection.cs index be11c326812cb5..3211d2c374b764 100644 --- a/src/libraries/System.Linq.Queryable/src/System/Linq/CachedReflection.cs +++ b/src/libraries/System.Linq.Queryable/src/System/Linq/CachedReflection.cs @@ -224,6 +224,18 @@ public static MethodInfo Distinct_TSource_2(Type TSource) => (s_Distinct_TSource_2 ??= new Func, IEqualityComparer, IQueryable>(Queryable.Distinct).GetMethodInfo().GetGenericMethodDefinition()) .MakeGenericMethod(TSource); + private static MethodInfo? s_DistinctBy_TSource_TKey_2; + + public static MethodInfo DistinctBy_TSource_TKey_2(Type TSource, Type TKey) => + (s_DistinctBy_TSource_TKey_2 ??= new Func, Expression>, IQueryable>(Queryable.DistinctBy).GetMethodInfo().GetGenericMethodDefinition()) + .MakeGenericMethod(TSource, TKey); + + private static MethodInfo? s_DistinctBy_TSource_TKey_3; + + public static MethodInfo DistinctBy_TSource_TKey_3(Type TSource, Type TKey) => + (s_DistinctBy_TSource_TKey_3 ??= new Func, Expression>, IEqualityComparer, IQueryable>(Queryable.DistinctBy).GetMethodInfo().GetGenericMethodDefinition()) + .MakeGenericMethod(TSource, TKey); + private static MethodInfo? s_ElementAt_Int32_TSource_2; public static MethodInfo ElementAt_Int32_TSource_2(Type TSource) => @@ -260,6 +272,18 @@ public static MethodInfo Except_TSource_3(Type TSource) => (s_Except_TSource_3 ??= new Func, IEnumerable, IEqualityComparer, IQueryable>(Queryable.Except).GetMethodInfo().GetGenericMethodDefinition()) .MakeGenericMethod(TSource); + private static MethodInfo? s_ExceptBy_TSource_TKey_3; + + public static MethodInfo ExceptBy_TSource_TKey_3(Type TSource, Type TKey) => + (s_ExceptBy_TSource_TKey_3 ??= new Func, IEnumerable, Expression>, IQueryable>(Queryable.ExceptBy).GetMethodInfo().GetGenericMethodDefinition()) + .MakeGenericMethod(TSource, TKey); + + private static MethodInfo? s_ExceptBy_TSource_TKey_4; + + public static MethodInfo ExceptBy_TSource_TKey_4(Type TSource, Type TKey) => + (s_ExceptBy_TSource_TKey_4 ??= new Func, IEnumerable, Expression>, IEqualityComparer, IQueryable>(Queryable.ExceptBy).GetMethodInfo().GetGenericMethodDefinition()) + .MakeGenericMethod(TSource, TKey); + private static MethodInfo? s_First_TSource_1; public static MethodInfo First_TSource_1(Type TSource) => @@ -370,6 +394,18 @@ public static MethodInfo Intersect_TSource_3(Type TSource) => (s_Intersect_TSource_3 ??= new Func, IEnumerable, IEqualityComparer, IQueryable>(Queryable.Intersect).GetMethodInfo().GetGenericMethodDefinition()) .MakeGenericMethod(TSource); + private static MethodInfo? s_IntersectBy_TSource_TKey_3; + + public static MethodInfo IntersectBy_TSource_TKey_3(Type TSource, Type TKey) => + (s_IntersectBy_TSource_TKey_3 ??= new Func, IEnumerable, Expression>, IQueryable>(Queryable.IntersectBy).GetMethodInfo().GetGenericMethodDefinition()) + .MakeGenericMethod(TSource, TKey); + + private static MethodInfo? s_IntersectBy_TSource_TKey_4; + + public static MethodInfo IntersectBy_TSource_TKey_4(Type TSource, Type TKey) => + (s_IntersectBy_TSource_TKey_4 ??= new Func, IEnumerable, Expression>, IEqualityComparer, IQueryable>(Queryable.IntersectBy).GetMethodInfo().GetGenericMethodDefinition()) + .MakeGenericMethod(TSource, TKey); + private static MethodInfo? s_Join_TOuter_TInner_TKey_TResult_5; public static MethodInfo Join_TOuter_TInner_TKey_TResult_5(Type TOuter, Type TInner, Type TKey, Type TResult) => @@ -438,24 +474,60 @@ public static MethodInfo Max_TSource_1(Type TSource) => (s_Max_TSource_1 ??= new Func, object?>(Queryable.Max).GetMethodInfo().GetGenericMethodDefinition()) .MakeGenericMethod(TSource); + private static MethodInfo? s_Max_TSource_2; + + public static MethodInfo Max_TSource_2(Type TSource) => + (s_Max_TSource_2 ??= new Func, IComparer, object?>(Queryable.Max).GetMethodInfo().GetGenericMethodDefinition()) + .MakeGenericMethod(TSource); + private static MethodInfo? s_Max_TSource_TResult_2; public static MethodInfo Max_TSource_TResult_2(Type TSource, Type TResult) => (s_Max_TSource_TResult_2 ??= new Func, Expression>, object?>(Queryable.Max).GetMethodInfo().GetGenericMethodDefinition()) .MakeGenericMethod(TSource, TResult); + private static MethodInfo? s_MaxBy_TSource_TKey_2; + + public static MethodInfo MaxBy_TSource_TKey_2(Type TSource, Type TKey) => + (s_MaxBy_TSource_TKey_2 ??= new Func, Expression>, object?>(Queryable.MaxBy).GetMethodInfo().GetGenericMethodDefinition()) + .MakeGenericMethod(TSource, TKey); + + private static MethodInfo? s_MaxBy_TSource_TKey_3; + + public static MethodInfo MaxBy_TSource_TKey_3(Type TSource, Type TKey) => + (s_MaxBy_TSource_TKey_3 ??= new Func, Expression>, IComparer, object?>(Queryable.MaxBy).GetMethodInfo().GetGenericMethodDefinition()) + .MakeGenericMethod(TSource, TKey); + private static MethodInfo? s_Min_TSource_1; public static MethodInfo Min_TSource_1(Type TSource) => (s_Min_TSource_1 ??= new Func, object?>(Queryable.Min).GetMethodInfo().GetGenericMethodDefinition()) .MakeGenericMethod(TSource); + private static MethodInfo? s_Min_TSource_2; + + public static MethodInfo Min_TSource_2(Type TSource) => + (s_Min_TSource_2 ??= new Func, IComparer, object?>(Queryable.Min).GetMethodInfo().GetGenericMethodDefinition()) + .MakeGenericMethod(TSource); + private static MethodInfo? s_Min_TSource_TResult_2; public static MethodInfo Min_TSource_TResult_2(Type TSource, Type TResult) => (s_Min_TSource_TResult_2 ??= new Func, Expression>, object?>(Queryable.Min).GetMethodInfo().GetGenericMethodDefinition()) .MakeGenericMethod(TSource, TResult); + private static MethodInfo? s_MinBy_TSource_TKey_2; + + public static MethodInfo MinBy_TSource_TKey_2(Type TSource, Type TKey) => + (s_MinBy_TSource_TKey_2 ??= new Func, Expression>, object?>(Queryable.MinBy).GetMethodInfo().GetGenericMethodDefinition()) + .MakeGenericMethod(TSource, TKey); + + private static MethodInfo? s_MinBy_TSource_TKey_3; + + public static MethodInfo MinBy_TSource_TKey_3(Type TSource, Type TKey) => + (s_MinBy_TSource_TKey_3 ??= new Func, Expression>, IComparer, object?>(Queryable.MinBy).GetMethodInfo().GetGenericMethodDefinition()) + .MakeGenericMethod(TSource, TKey); + private static MethodInfo? s_OfType_TResult_1; public static MethodInfo OfType_TResult_1(Type TResult) => @@ -766,6 +838,18 @@ public static MethodInfo Union_TSource_3(Type TSource) => (s_Union_TSource_3 ??= new Func, IEnumerable, IEqualityComparer, IQueryable>(Queryable.Union).GetMethodInfo().GetGenericMethodDefinition()) .MakeGenericMethod(TSource); + private static MethodInfo? s_UnionBy_TSource_TKey_3; + + public static MethodInfo UnionBy_TSource_TKey_3(Type TSource, Type TKey) => + (s_UnionBy_TSource_TKey_3 ??= new Func, IEnumerable, Expression>, IQueryable>(Queryable.UnionBy).GetMethodInfo().GetGenericMethodDefinition()) + .MakeGenericMethod(TSource, TKey); + + private static MethodInfo? s_UnionBy_TSource_TKey_4; + + public static MethodInfo UnionBy_TSource_TKey_4(Type TSource, Type TKey) => + (s_UnionBy_TSource_TKey_4 ??= new Func, IEnumerable, Expression>, IEqualityComparer, IQueryable>(Queryable.UnionBy).GetMethodInfo().GetGenericMethodDefinition()) + .MakeGenericMethod(TSource, TKey); + private static MethodInfo? s_Where_TSource_2; public static MethodInfo Where_TSource_2(Type TSource) => diff --git a/src/libraries/System.Linq.Queryable/src/System/Linq/Queryable.cs b/src/libraries/System.Linq.Queryable/src/System/Linq/Queryable.cs index 36a7789d464f84..2917955e9d2695 100644 --- a/src/libraries/System.Linq.Queryable/src/System/Linq/Queryable.cs +++ b/src/libraries/System.Linq.Queryable/src/System/Linq/Queryable.cs @@ -639,6 +639,36 @@ public static IQueryable Distinct(this IQueryable sou )); } + [DynamicDependency("DistinctBy`2", typeof(Enumerable))] + public static IQueryable DistinctBy(this IQueryable source, Expression> keySelector) + { + if (source == null) + throw Error.ArgumentNull(nameof(source)); + if (keySelector == null) + throw Error.ArgumentNull(nameof(keySelector)); + return source.Provider.CreateQuery( + Expression.Call( + null, + CachedReflectionInfo.DistinctBy_TSource_TKey_2(typeof(TSource), typeof(TKey)), + source.Expression, Expression.Quote(keySelector) + )); + } + + [DynamicDependency("DistinctBy`2", typeof(Enumerable))] + public static IQueryable DistinctBy(this IQueryable source, Expression> keySelector, IEqualityComparer? comparer) + { + if (source == null) + throw Error.ArgumentNull(nameof(source)); + if (keySelector == null) + throw Error.ArgumentNull(nameof(keySelector)); + return source.Provider.CreateQuery( + Expression.Call( + null, + CachedReflectionInfo.DistinctBy_TSource_TKey_3(typeof(TSource), typeof(TKey)), + source.Expression, Expression.Quote(keySelector), Expression.Constant(comparer, typeof(IEqualityComparer)) + )); + } + [DynamicDependency("Chunk`1", typeof(Enumerable))] public static IQueryable Chunk(this IQueryable source, int size) { @@ -763,6 +793,43 @@ public static IQueryable Union(this IQueryable source )); } + [DynamicDependency("UnionBy`2", typeof(Enumerable))] + public static IQueryable UnionBy(this IQueryable source1, IEnumerable source2, Expression> keySelector) + { + if (source1 == null) + throw Error.ArgumentNull(nameof(source1)); + if (source2 == null) + throw Error.ArgumentNull(nameof(source2)); + if (keySelector == null) + throw Error.ArgumentNull(nameof(keySelector)); + return source1.Provider.CreateQuery( + Expression.Call( + null, + CachedReflectionInfo.UnionBy_TSource_TKey_3(typeof(TSource), typeof(TKey)), + source1.Expression, GetSourceExpression(source2), Expression.Quote(keySelector) + )); + } + + [DynamicDependency("UnionBy`2", typeof(Enumerable))] + public static IQueryable UnionBy(this IQueryable source1, IEnumerable source2, Expression> keySelector, IEqualityComparer? comparer) + { + if (source1 == null) + throw Error.ArgumentNull(nameof(source1)); + if (source2 == null) + throw Error.ArgumentNull(nameof(source2)); + if (keySelector == null) + throw Error.ArgumentNull(nameof(keySelector)); + return source1.Provider.CreateQuery( + Expression.Call( + null, + CachedReflectionInfo.UnionBy_TSource_TKey_4(typeof(TSource), typeof(TKey)), + source1.Expression, + GetSourceExpression(source2), + Expression.Quote(keySelector), + Expression.Constant(comparer, typeof(IEqualityComparer)) + )); + } + [DynamicDependency("Intersect`1", typeof(Enumerable))] public static IQueryable Intersect(this IQueryable source1, IEnumerable source2) { @@ -795,6 +862,45 @@ public static IQueryable Intersect(this IQueryable so )); } + [DynamicDependency("IntersectBy`2", typeof(Enumerable))] + public static IQueryable IntersectBy(this IQueryable source1, IEnumerable source2, Expression> keySelector) + { + if (source1 == null) + throw Error.ArgumentNull(nameof(source1)); + if (source2 == null) + throw Error.ArgumentNull(nameof(source2)); + if (keySelector == null) + throw Error.ArgumentNull(nameof(keySelector)); + return source1.Provider.CreateQuery( + Expression.Call( + null, + CachedReflectionInfo.IntersectBy_TSource_TKey_3(typeof(TSource), typeof(TKey)), + source1.Expression, + GetSourceExpression(source2), + Expression.Quote(keySelector) + )); + } + + [DynamicDependency("IntersectBy`2", typeof(Enumerable))] + public static IQueryable IntersectBy(this IQueryable source1, IEnumerable source2, Expression> keySelector, IEqualityComparer? comparer) + { + if (source1 == null) + throw Error.ArgumentNull(nameof(source1)); + if (source2 == null) + throw Error.ArgumentNull(nameof(source2)); + if (keySelector == null) + throw Error.ArgumentNull(nameof(keySelector)); + return source1.Provider.CreateQuery( + Expression.Call( + null, + CachedReflectionInfo.IntersectBy_TSource_TKey_4(typeof(TSource), typeof(TKey)), + source1.Expression, + GetSourceExpression(source2), + Expression.Quote(keySelector), + Expression.Constant(comparer, typeof(IEqualityComparer)) + )); + } + [DynamicDependency("Except`1", typeof(Enumerable))] public static IQueryable Except(this IQueryable source1, IEnumerable source2) { @@ -827,6 +933,45 @@ public static IQueryable Except(this IQueryable sourc )); } + [DynamicDependency("ExceptBy`2", typeof(Enumerable))] + public static IQueryable ExceptBy(this IQueryable source1, IEnumerable source2, Expression> keySelector) + { + if (source1 == null) + throw Error.ArgumentNull(nameof(source1)); + if (source2 == null) + throw Error.ArgumentNull(nameof(source2)); + if (keySelector == null) + throw Error.ArgumentNull(nameof(keySelector)); + return source1.Provider.CreateQuery( + Expression.Call( + null, + CachedReflectionInfo.ExceptBy_TSource_TKey_3(typeof(TSource), typeof(TKey)), + source1.Expression, + GetSourceExpression(source2), + Expression.Quote(keySelector) + )); + } + + [DynamicDependency("ExceptBy`2", typeof(Enumerable))] + public static IQueryable ExceptBy(this IQueryable source1, IEnumerable source2, Expression> keySelector, IEqualityComparer? comparer) + { + if (source1 == null) + throw Error.ArgumentNull(nameof(source1)); + if (source2 == null) + throw Error.ArgumentNull(nameof(source2)); + if (keySelector == null) + throw Error.ArgumentNull(nameof(keySelector)); + return source1.Provider.CreateQuery( + Expression.Call( + null, + CachedReflectionInfo.ExceptBy_TSource_TKey_4(typeof(TSource), typeof(TKey)), + source1.Expression, + GetSourceExpression(source2), + Expression.Quote(keySelector), + Expression.Constant(comparer, typeof(IEqualityComparer)) + )); + } + [DynamicDependency("First`1", typeof(Enumerable))] public static TSource First(this IQueryable source) { @@ -1339,6 +1484,20 @@ public static long LongCount(this IQueryable source, Expressio CachedReflectionInfo.Min_TSource_1(typeof(TSource)), source.Expression)); } + [DynamicDependency("Min`1", typeof(Enumerable))] + public static TSource? Min(this IQueryable source, IComparer? comparer) + { + if (source == null) + throw Error.ArgumentNull(nameof(source)); + return source.Provider.Execute( + Expression.Call( + null, + CachedReflectionInfo.Min_TSource_2(typeof(TSource)), + source.Expression, + Expression.Constant(comparer, typeof(IComparer)) + )); + } + [DynamicDependency("Min`2", typeof(Enumerable))] public static TResult? Min(this IQueryable source, Expression> selector) { @@ -1354,6 +1513,39 @@ public static long LongCount(this IQueryable source, Expressio )); } + [DynamicDependency("MinBy`2", typeof(Enumerable))] + public static TSource? MinBy(this IQueryable source, Expression> keySelector) + { + if (source == null) + throw Error.ArgumentNull(nameof(source)); + if (keySelector == null) + throw Error.ArgumentNull(nameof(keySelector)); + return source.Provider.Execute( + Expression.Call( + null, + CachedReflectionInfo.MinBy_TSource_TKey_2(typeof(TSource), typeof(TKey)), + source.Expression, + Expression.Quote(keySelector) + )); + } + + [DynamicDependency("MinBy`2", typeof(Enumerable))] + public static TSource? MinBy(this IQueryable source, Expression> keySelector, IComparer? comparer) + { + if (source == null) + throw Error.ArgumentNull(nameof(source)); + if (keySelector == null) + throw Error.ArgumentNull(nameof(keySelector)); + return source.Provider.Execute( + Expression.Call( + null, + CachedReflectionInfo.MinBy_TSource_TKey_3(typeof(TSource), typeof(TKey)), + source.Expression, + Expression.Quote(keySelector), + Expression.Constant(comparer, typeof(IComparer)) + )); + } + [DynamicDependency("Max`1", typeof(Enumerable))] public static TSource? Max(this IQueryable source) { @@ -1365,6 +1557,20 @@ public static long LongCount(this IQueryable source, Expressio CachedReflectionInfo.Max_TSource_1(typeof(TSource)), source.Expression)); } + [DynamicDependency("Max`1", typeof(Enumerable))] + public static TSource? Max(this IQueryable source, IComparer? comparer) + { + if (source == null) + throw Error.ArgumentNull(nameof(source)); + return source.Provider.Execute( + Expression.Call( + null, + CachedReflectionInfo.Max_TSource_2(typeof(TSource)), + source.Expression, + Expression.Constant(comparer, typeof(IComparer)) + )); + } + [DynamicDependency("Max`2", typeof(Enumerable))] public static TResult? Max(this IQueryable source, Expression> selector) { @@ -1380,6 +1586,39 @@ public static long LongCount(this IQueryable source, Expressio )); } + [DynamicDependency("MaxBy`2", typeof(Enumerable))] + public static TSource? MaxBy(this IQueryable source, Expression> keySelector) + { + if (source == null) + throw Error.ArgumentNull(nameof(source)); + if (keySelector == null) + throw Error.ArgumentNull(nameof(keySelector)); + return source.Provider.Execute( + Expression.Call( + null, + CachedReflectionInfo.MaxBy_TSource_TKey_2(typeof(TSource), typeof(TKey)), + source.Expression, + Expression.Quote(keySelector) + )); + } + + [DynamicDependency("MaxBy`2", typeof(Enumerable))] + public static TSource? MaxBy(this IQueryable source, Expression> keySelector, IComparer? comparer) + { + if (source == null) + throw Error.ArgumentNull(nameof(source)); + if (keySelector == null) + throw Error.ArgumentNull(nameof(keySelector)); + return source.Provider.Execute( + Expression.Call( + null, + CachedReflectionInfo.MaxBy_TSource_TKey_3(typeof(TSource), typeof(TKey)), + source.Expression, + Expression.Quote(keySelector), + Expression.Constant(comparer, typeof(IComparer)) + )); + } + [DynamicDependency("Sum", typeof(Enumerable))] public static int Sum(this IQueryable source) { diff --git a/src/libraries/System.Linq.Queryable/tests/DistinctTests.cs b/src/libraries/System.Linq.Queryable/tests/DistinctTests.cs index b252b287bfde60..44456901990320 100644 --- a/src/libraries/System.Linq.Queryable/tests/DistinctTests.cs +++ b/src/libraries/System.Linq.Queryable/tests/DistinctTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Linq.Expressions; using Xunit; namespace System.Linq.Tests @@ -77,5 +78,40 @@ public void Distinct2() var count = (new int[] { 0, 1, 2, 2, 0 }).AsQueryable().Distinct(EqualityComparer.Default).Count(); Assert.Equal(3, count); } + + [Fact] + public void DistinctBy_NullSource_ThrowsArgumentNullException() + { + IQueryable source = null; + + AssertExtensions.Throws("source", () => source.DistinctBy(x => x)); + AssertExtensions.Throws("source", () => source.DistinctBy(x => x, EqualityComparer.Default)); + } + + [Fact] + public void DistinctBy_NullKeySelector_ThrowsArgumentNullException() + { + IQueryable source = Enumerable.Empty().AsQueryable(); + Expression> keySelector = null; + + AssertExtensions.Throws("keySelector", () => source.DistinctBy(keySelector)); + AssertExtensions.Throws("keySelector", () => source.DistinctBy(keySelector, EqualityComparer.Default)); + } + + [Fact] + public void DistinctBy() + { + var expected = Enumerable.Range(0, 3); + var actual = Enumerable.Range(0, 20).AsQueryable().DistinctBy(x => x % 3).ToArray(); + Assert.Equal(expected, actual); + } + + [Fact] + public void DistinctBy_CustomComparison() + { + var expected = Enumerable.Range(0, 3); + var actual = Enumerable.Range(0, 20).AsQueryable().DistinctBy(x => x % 3, EqualityComparer.Default).ToArray(); + Assert.Equal(expected, actual); + } } } diff --git a/src/libraries/System.Linq.Queryable/tests/ExceptTests.cs b/src/libraries/System.Linq.Queryable/tests/ExceptTests.cs index b7aa76cdd463b4..7ccd478d1b29f0 100644 --- a/src/libraries/System.Linq.Queryable/tests/ExceptTests.cs +++ b/src/libraries/System.Linq.Queryable/tests/ExceptTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Linq.Expressions; using Xunit; namespace System.Linq.Tests @@ -85,5 +86,50 @@ public void Except2() var count = (new int[] { 0, 1, 2 }).AsQueryable().Except((new int[] { 1, 2, 3 }).AsQueryable(), EqualityComparer.Default).Count(); Assert.Equal(1, count); } + + [Fact] + public void ExceptBy_NullSource1_ThrowsArgumentNullException() + { + IQueryable source1 = null; + + AssertExtensions.Throws("source1", () => source1.ExceptBy(Enumerable.Empty(), x => x)); + AssertExtensions.Throws("source1", () => source1.ExceptBy(Enumerable.Empty(), x => x, EqualityComparer.Default)); + } + + [Fact] + public void ExceptBy_NullSource2_ThrowsArgumentNullException() + { + IQueryable source1 = Enumerable.Empty().AsQueryable(); + IQueryable source2 = null; + + AssertExtensions.Throws("source2", () => source1.ExceptBy(source2, x => x)); + AssertExtensions.Throws("source2", () => source1.ExceptBy(source2, x => x, EqualityComparer.Default)); + } + + [Fact] + public void ExceptBy_NullKeySelector_ThrowsArgumentNullException() + { + IQueryable source = Enumerable.Empty().AsQueryable(); + Expression> keySelector = null; + + AssertExtensions.Throws("keySelector", () => source.ExceptBy(source, keySelector)); + AssertExtensions.Throws("keySelector", () => source.ExceptBy(source, keySelector, EqualityComparer.Default)); + } + + [Fact] + public void ExceptBy() + { + var expected = Enumerable.Range(5, 5); + var actual = Enumerable.Range(0, 10).AsQueryable().ExceptBy(Enumerable.Range(0, 5), x => x).ToArray(); + Assert.Equal(expected, actual); + } + + [Fact] + public void ExceptBy_CustomComparison() + { + var expected = Enumerable.Range(5, 5); + var actual = Enumerable.Range(0, 10).AsQueryable().ExceptBy(Enumerable.Range(0, 5), x => x, EqualityComparer.Default).ToArray(); + Assert.Equal(expected, actual); + } } } diff --git a/src/libraries/System.Linq.Queryable/tests/IntersectTests.cs b/src/libraries/System.Linq.Queryable/tests/IntersectTests.cs index 0731d99aa8b425..184a618f64ef3b 100644 --- a/src/libraries/System.Linq.Queryable/tests/IntersectTests.cs +++ b/src/libraries/System.Linq.Queryable/tests/IntersectTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Linq.Expressions; using Xunit; namespace System.Linq.Tests @@ -83,5 +84,50 @@ public void Intersect2() var count = (new int[] { 0, 1, 2 }).AsQueryable().Intersect((new int[] { 1, 2, 3 }).AsQueryable(), EqualityComparer.Default).Count(); Assert.Equal(2, count); } + + [Fact] + public void IntersectBy_NullSource1_ThrowsArgumentNullException() + { + IQueryable source1 = null; + + AssertExtensions.Throws("source1", () => source1.IntersectBy(Enumerable.Empty(), x => x)); + AssertExtensions.Throws("source1", () => source1.IntersectBy(Enumerable.Empty(), x => x, EqualityComparer.Default)); + } + + [Fact] + public void IntersectBy_NullSource2_ThrowsArgumentNullException() + { + IQueryable source1 = Enumerable.Empty().AsQueryable(); + IQueryable source2 = null; + + AssertExtensions.Throws("source2", () => source1.IntersectBy(source2, x => x)); + AssertExtensions.Throws("source2", () => source1.IntersectBy(source2, x => x, EqualityComparer.Default)); + } + + [Fact] + public void IntersectBy_NullKeySelector_ThrowsArgumentNullException() + { + IQueryable source = Enumerable.Empty().AsQueryable(); + Expression> keySelector = null; + + AssertExtensions.Throws("keySelector", () => source.IntersectBy(source, keySelector)); + AssertExtensions.Throws("keySelector", () => source.IntersectBy(source, keySelector, EqualityComparer.Default)); + } + + [Fact] + public void IntersectBy() + { + var expected = Enumerable.Range(5, 5); + var actual = Enumerable.Range(0, 10).AsQueryable().IntersectBy(Enumerable.Range(5, 20), x => x).ToArray(); + Assert.Equal(expected, actual); + } + + [Fact] + public void IntersectBy_CustomComparison() + { + var expected = Enumerable.Range(5, 5); + var actual = Enumerable.Range(0, 10).AsQueryable().IntersectBy(Enumerable.Range(5, 20), x => x, EqualityComparer.Default).ToArray(); + Assert.Equal(expected, actual); + } } } diff --git a/src/libraries/System.Linq.Queryable/tests/MaxTests.cs b/src/libraries/System.Linq.Queryable/tests/MaxTests.cs index 4354d895d3ee33..84a56d5b856d5a 100644 --- a/src/libraries/System.Linq.Queryable/tests/MaxTests.cs +++ b/src/libraries/System.Linq.Queryable/tests/MaxTests.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; using System.Linq.Expressions; using Xunit; @@ -589,5 +590,53 @@ public void Max2() var val = (new int[] { 0, 2, 1 }).AsQueryable().Max(n => n); Assert.Equal(2, val); } + + [Fact] + public void Max_CustomComparer_NullSource_ThrowsArgumentNullException() + { + IQueryable source = null; + AssertExtensions.Throws("source", () => source.Max(Comparer.Default)); + } + + [Fact] + public void Max_CustomComparer() + { + IComparer comparer = Comparer.Create((x, y) => -x.CompareTo(y)); + IQueryable source = Enumerable.Range(1, 10).AsQueryable(); + Assert.Equal(1, source.Max(comparer)); + } + + [Fact] + public void MaxBy_NullSource_ThrowsArgumentNullException() + { + IQueryable source = null; + + AssertExtensions.Throws("source", () => source.MaxBy(x => x)); + AssertExtensions.Throws("source", () => source.MaxBy(x => x, Comparer.Default)); + } + + [Fact] + public void MaxBy_NullKeySelector_ThrowsArgumentNullException() + { + IQueryable source = Enumerable.Empty().AsQueryable(); + Expression> keySelector = null; + + AssertExtensions.Throws("keySelector", () => source.MaxBy(keySelector)); + AssertExtensions.Throws("keySelector", () => source.MaxBy(keySelector, Comparer.Default)); + } + + [Fact] + public void MaxBy() + { + IQueryable source = Enumerable.Range(1, 20).AsQueryable(); + Assert.Equal(1, source.MaxBy(x => -x)); + } + + [Fact] + public void MaxBy_CustomComparer() + { + IQueryable source = Enumerable.Range(1, 20).AsQueryable(); + Assert.Equal(20, source.MaxBy(x => -x, Comparer.Create((x, y) => -x.CompareTo(y)))); + } } } diff --git a/src/libraries/System.Linq.Queryable/tests/MinTests.cs b/src/libraries/System.Linq.Queryable/tests/MinTests.cs index 10bf12b4e17c97..8e9a8a6680b869 100644 --- a/src/libraries/System.Linq.Queryable/tests/MinTests.cs +++ b/src/libraries/System.Linq.Queryable/tests/MinTests.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; using System.Linq.Expressions; using Xunit; @@ -557,5 +558,53 @@ public void Min2() var val = (new int[] { 0, 2, 1 }).AsQueryable().Min(n => n); Assert.Equal(0, val); } + + [Fact] + public void Min_CustomComparer_NullSource_ThrowsArgumentNullException() + { + IQueryable source = null; + AssertExtensions.Throws("source", () => source.Min(Comparer.Default)); + } + + [Fact] + public void Min_CustomComparer() + { + IComparer comparer = Comparer.Create((x, y) => -x.CompareTo(y)); + IQueryable source = Enumerable.Range(1, 10).AsQueryable(); + Assert.Equal(10, source.Min(comparer)); + } + + [Fact] + public void MinBy_NullSource_ThrowsArgumentNullException() + { + IQueryable source = null; + + AssertExtensions.Throws("source", () => source.MinBy(x => x)); + AssertExtensions.Throws("source", () => source.MinBy(x => x, Comparer.Default)); + } + + [Fact] + public void MinBy_NullKeySelector_ThrowsArgumentNullException() + { + IQueryable source = Enumerable.Empty().AsQueryable(); + Expression> keySelector = null; + + AssertExtensions.Throws("keySelector", () => source.MinBy(keySelector)); + AssertExtensions.Throws("keySelector", () => source.MinBy(keySelector, Comparer.Default)); + } + + [Fact] + public void MinBy() + { + IQueryable source = Enumerable.Range(1, 20).AsQueryable(); + Assert.Equal(20, source.MinBy(x => -x)); + } + + [Fact] + public void MinBy_CustomComparer() + { + IQueryable source = Enumerable.Range(1, 20).AsQueryable(); + Assert.Equal(1, source.MinBy(x => -x, Comparer.Create((x, y) => -x.CompareTo(y)))); + } } } diff --git a/src/libraries/System.Linq.Queryable/tests/TrimCompatibilityTests.cs b/src/libraries/System.Linq.Queryable/tests/TrimCompatibilityTests.cs index b1463804c69086..6a4583253edbec 100644 --- a/src/libraries/System.Linq.Queryable/tests/TrimCompatibilityTests.cs +++ b/src/libraries/System.Linq.Queryable/tests/TrimCompatibilityTests.cs @@ -61,7 +61,7 @@ public static void CachedReflectionInfoMethodsNoAnnotations() .Where(m => m.GetParameters().Length > 0); // If you are adding a new method to this class, ensure the method meets these requirements - Assert.Equal(117, methods.Count()); + Assert.Equal(131, methods.Count()); foreach (MethodInfo method in methods) { ParameterInfo[] parameters = method.GetParameters(); diff --git a/src/libraries/System.Linq.Queryable/tests/UnionTests.cs b/src/libraries/System.Linq.Queryable/tests/UnionTests.cs index fe63038371739e..1d6d9ae6afe0a2 100644 --- a/src/libraries/System.Linq.Queryable/tests/UnionTests.cs +++ b/src/libraries/System.Linq.Queryable/tests/UnionTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Linq.Expressions; using Xunit; namespace System.Linq.Tests @@ -79,5 +80,50 @@ public void Union2() var count = (new int[] { 0, 1, 2 }).AsQueryable().Union((new int[] { 1, 2, 3 }).AsQueryable(), EqualityComparer.Default).Count(); Assert.Equal(4, count); } + + [Fact] + public void UnionBy_NullSource1_ThrowsArgumentNullException() + { + IQueryable source1 = null; + + AssertExtensions.Throws("source1", () => source1.UnionBy(Enumerable.Empty(), x => x)); + AssertExtensions.Throws("source1", () => source1.UnionBy(Enumerable.Empty(), x => x, EqualityComparer.Default)); + } + + [Fact] + public void UnionBy_NullSource2_ThrowsArgumentNullException() + { + IQueryable source1 = Enumerable.Empty().AsQueryable(); + IQueryable source2 = null; + + AssertExtensions.Throws("source2", () => source1.UnionBy(source2, x => x)); + AssertExtensions.Throws("source2", () => source1.UnionBy(source2, x => x, EqualityComparer.Default)); + } + + [Fact] + public void UnionBy_NullKeySelector_ThrowsArgumentNullException() + { + IQueryable source = Enumerable.Empty().AsQueryable(); + Expression> keySelector = null; + + AssertExtensions.Throws("keySelector", () => source.UnionBy(source, keySelector)); + AssertExtensions.Throws("keySelector", () => source.UnionBy(source, keySelector, EqualityComparer.Default)); + } + + [Fact] + public void UnionBy() + { + var expected = Enumerable.Range(0, 10); + var actual = Enumerable.Range(0, 5).AsQueryable().UnionBy(Enumerable.Range(5, 5), x => x).ToArray(); + Assert.Equal(expected, actual); + } + + [Fact] + public void UnionBy_CustomComparison() + { + var expected = Enumerable.Range(0, 10); + var actual = Enumerable.Range(0, 5).AsQueryable().UnionBy(Enumerable.Range(5, 5), x => x, EqualityComparer.Default).ToArray(); + Assert.Equal(expected, actual); + } } } diff --git a/src/libraries/System.Linq/ref/System.Linq.cs b/src/libraries/System.Linq/ref/System.Linq.cs index 0f09240a0249de..5b2d9996512aea 100644 --- a/src/libraries/System.Linq/ref/System.Linq.cs +++ b/src/libraries/System.Linq/ref/System.Linq.cs @@ -49,13 +49,17 @@ public static System.Collections.Generic.IEnumerable< public static int Count(this System.Collections.Generic.IEnumerable source, System.Func predicate) { throw null; } public static System.Collections.Generic.IEnumerable DefaultIfEmpty(this System.Collections.Generic.IEnumerable source) { throw null; } public static System.Collections.Generic.IEnumerable DefaultIfEmpty(this System.Collections.Generic.IEnumerable source, TSource defaultValue) { throw null; } + public static System.Collections.Generic.IEnumerable DistinctBy(this System.Collections.Generic.IEnumerable source, System.Func keySelector) { throw null; } + public static System.Collections.Generic.IEnumerable DistinctBy(this System.Collections.Generic.IEnumerable source, System.Func keySelector, System.Collections.Generic.IEqualityComparer? comparer) { throw null; } public static System.Collections.Generic.IEnumerable Distinct(this System.Collections.Generic.IEnumerable source) { throw null; } public static System.Collections.Generic.IEnumerable Distinct(this System.Collections.Generic.IEnumerable source, System.Collections.Generic.IEqualityComparer? comparer) { throw null; } - public static TSource? ElementAtOrDefault(this System.Collections.Generic.IEnumerable source, int index) { throw null; } public static TSource? ElementAtOrDefault(this System.Collections.Generic.IEnumerable source, System.Index index) { throw null; } - public static TSource ElementAt(this System.Collections.Generic.IEnumerable source, int index) { throw null; } + public static TSource? ElementAtOrDefault(this System.Collections.Generic.IEnumerable source, int index) { throw null; } public static TSource ElementAt(this System.Collections.Generic.IEnumerable source, System.Index index) { throw null; } + public static TSource ElementAt(this System.Collections.Generic.IEnumerable source, int index) { throw null; } public static System.Collections.Generic.IEnumerable Empty() { throw null; } + public static System.Collections.Generic.IEnumerable ExceptBy(this System.Collections.Generic.IEnumerable first, System.Collections.Generic.IEnumerable second, System.Func keySelector) { throw null; } + public static System.Collections.Generic.IEnumerable ExceptBy(this System.Collections.Generic.IEnumerable first, System.Collections.Generic.IEnumerable second, System.Func keySelector, System.Collections.Generic.IEqualityComparer? comparer) { throw null; } public static System.Collections.Generic.IEnumerable Except(this System.Collections.Generic.IEnumerable first, System.Collections.Generic.IEnumerable second) { throw null; } public static System.Collections.Generic.IEnumerable Except(this System.Collections.Generic.IEnumerable first, System.Collections.Generic.IEnumerable second, System.Collections.Generic.IEqualityComparer? comparer) { throw null; } public static TSource? FirstOrDefault(this System.Collections.Generic.IEnumerable source) { throw null; } @@ -74,6 +78,8 @@ public static System.Collections.Generic.IEnumerable< public static System.Collections.Generic.IEnumerable GroupBy(this System.Collections.Generic.IEnumerable source, System.Func keySelector, System.Func elementSelector, System.Func, TResult> resultSelector, System.Collections.Generic.IEqualityComparer? comparer) { throw null; } public static System.Collections.Generic.IEnumerable GroupJoin(this System.Collections.Generic.IEnumerable outer, System.Collections.Generic.IEnumerable inner, System.Func outerKeySelector, System.Func innerKeySelector, System.Func, TResult> resultSelector) { throw null; } public static System.Collections.Generic.IEnumerable GroupJoin(this System.Collections.Generic.IEnumerable outer, System.Collections.Generic.IEnumerable inner, System.Func outerKeySelector, System.Func innerKeySelector, System.Func, TResult> resultSelector, System.Collections.Generic.IEqualityComparer? comparer) { throw null; } + public static System.Collections.Generic.IEnumerable IntersectBy(this System.Collections.Generic.IEnumerable first, System.Collections.Generic.IEnumerable second, System.Func keySelector) { throw null; } + public static System.Collections.Generic.IEnumerable IntersectBy(this System.Collections.Generic.IEnumerable first, System.Collections.Generic.IEnumerable second, System.Func keySelector, System.Collections.Generic.IEqualityComparer? comparer) { throw null; } public static System.Collections.Generic.IEnumerable Intersect(this System.Collections.Generic.IEnumerable first, System.Collections.Generic.IEnumerable second) { throw null; } public static System.Collections.Generic.IEnumerable Intersect(this System.Collections.Generic.IEnumerable first, System.Collections.Generic.IEnumerable second, System.Collections.Generic.IEqualityComparer? comparer) { throw null; } public static System.Collections.Generic.IEnumerable Join(this System.Collections.Generic.IEnumerable outer, System.Collections.Generic.IEnumerable inner, System.Func outerKeySelector, System.Func innerKeySelector, System.Func resultSelector) { throw null; } @@ -96,7 +102,10 @@ public static System.Collections.Generic.IEnumerable< public static long? Max(this System.Collections.Generic.IEnumerable source) { throw null; } public static float? Max(this System.Collections.Generic.IEnumerable source) { throw null; } public static float Max(this System.Collections.Generic.IEnumerable source) { throw null; } + public static TSource? MaxBy(this System.Collections.Generic.IEnumerable source, System.Func keySelector) { throw null; } + public static TSource? MaxBy(this System.Collections.Generic.IEnumerable source, System.Func keySelector, System.Collections.Generic.IComparer? comparer) { throw null; } public static TSource? Max(this System.Collections.Generic.IEnumerable source) { throw null; } + public static TSource? Max(this System.Collections.Generic.IEnumerable source, System.Collections.Generic.IComparer? comparer) { throw null; } public static decimal Max(this System.Collections.Generic.IEnumerable source, System.Func selector) { throw null; } public static double Max(this System.Collections.Generic.IEnumerable source, System.Func selector) { throw null; } public static int Max(this System.Collections.Generic.IEnumerable source, System.Func selector) { throw null; } @@ -118,7 +127,10 @@ public static System.Collections.Generic.IEnumerable< public static long? Min(this System.Collections.Generic.IEnumerable source) { throw null; } public static float? Min(this System.Collections.Generic.IEnumerable source) { throw null; } public static float Min(this System.Collections.Generic.IEnumerable source) { throw null; } + public static TSource? MinBy(this System.Collections.Generic.IEnumerable source, System.Func keySelector) { throw null; } + public static TSource? MinBy(this System.Collections.Generic.IEnumerable source, System.Func keySelector, System.Collections.Generic.IComparer? comparer) { throw null; } public static TSource? Min(this System.Collections.Generic.IEnumerable source) { throw null; } + public static TSource? Min(this System.Collections.Generic.IEnumerable source, System.Collections.Generic.IComparer? comparer) { throw null; } public static decimal Min(this System.Collections.Generic.IEnumerable source, System.Func selector) { throw null; } public static double Min(this System.Collections.Generic.IEnumerable source, System.Func selector) { throw null; } public static int Min(this System.Collections.Generic.IEnumerable source, System.Func selector) { throw null; } @@ -199,6 +211,8 @@ public static System.Collections.Generic.IEnumerable< public static System.Linq.ILookup ToLookup(this System.Collections.Generic.IEnumerable source, System.Func keySelector, System.Func elementSelector) { throw null; } public static System.Linq.ILookup ToLookup(this System.Collections.Generic.IEnumerable source, System.Func keySelector, System.Func elementSelector, System.Collections.Generic.IEqualityComparer? comparer) { throw null; } public static bool TryGetNonEnumeratedCount(this System.Collections.Generic.IEnumerable source, out int count) { throw null; } + public static System.Collections.Generic.IEnumerable UnionBy(this System.Collections.Generic.IEnumerable first, System.Collections.Generic.IEnumerable second, System.Func keySelector) { throw null; } + public static System.Collections.Generic.IEnumerable UnionBy(this System.Collections.Generic.IEnumerable first, System.Collections.Generic.IEnumerable second, System.Func keySelector, System.Collections.Generic.IEqualityComparer? comparer) { throw null; } public static System.Collections.Generic.IEnumerable Union(this System.Collections.Generic.IEnumerable first, System.Collections.Generic.IEnumerable second) { throw null; } public static System.Collections.Generic.IEnumerable Union(this System.Collections.Generic.IEnumerable first, System.Collections.Generic.IEnumerable second, System.Collections.Generic.IEqualityComparer? comparer) { throw null; } public static System.Collections.Generic.IEnumerable Where(this System.Collections.Generic.IEnumerable source, System.Func predicate) { throw null; } diff --git a/src/libraries/System.Linq/src/System/Linq/Distinct.cs b/src/libraries/System.Linq/src/System/Linq/Distinct.cs index 5c59fc8164d32c..008128024302bf 100644 --- a/src/libraries/System.Linq/src/System/Linq/Distinct.cs +++ b/src/libraries/System.Linq/src/System/Linq/Distinct.cs @@ -20,6 +20,41 @@ public static IEnumerable Distinct(this IEnumerable s return new DistinctIterator(source, comparer); } + public static IEnumerable DistinctBy(this IEnumerable source, Func keySelector) => DistinctBy(source, keySelector, null); + + public static IEnumerable DistinctBy(this IEnumerable source, Func keySelector, IEqualityComparer? comparer) + { + if (source is null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source); + } + if (keySelector is null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.keySelector); + } + + return DistinctByIterator(source, keySelector, comparer); + } + + private static IEnumerable DistinctByIterator(IEnumerable source, Func keySelector, IEqualityComparer? comparer) + { + using IEnumerator enumerator = source.GetEnumerator(); + + if (enumerator.MoveNext()) + { + var set = new HashSet(DefaultInternalSetCapacity, comparer); + do + { + TSource element = enumerator.Current; + if (set.Add(keySelector(element))) + { + yield return element; + } + } + while (enumerator.MoveNext()); + } + } + /// /// An iterator that yields the distinct values in an . /// diff --git a/src/libraries/System.Linq/src/System/Linq/Except.cs b/src/libraries/System.Linq/src/System/Linq/Except.cs index b3e0f45075d2d1..f590cfd5488d0b 100644 --- a/src/libraries/System.Linq/src/System/Linq/Except.cs +++ b/src/libraries/System.Linq/src/System/Linq/Except.cs @@ -37,6 +37,26 @@ public static IEnumerable Except(this IEnumerable fir return ExceptIterator(first, second, comparer); } + public static IEnumerable ExceptBy(this IEnumerable first, IEnumerable second, Func keySelector) => ExceptBy(first, second, keySelector, null); + + public static IEnumerable ExceptBy(this IEnumerable first, IEnumerable second, Func keySelector, IEqualityComparer? comparer) + { + if (first is null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.first); + } + if (second is null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.second); + } + if (keySelector is null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.keySelector); + } + + return ExceptByIterator(first, second, keySelector, comparer); + } + private static IEnumerable ExceptIterator(IEnumerable first, IEnumerable second, IEqualityComparer? comparer) { var set = new HashSet(second, comparer); @@ -49,5 +69,18 @@ private static IEnumerable ExceptIterator(IEnumerable } } } + + private static IEnumerable ExceptByIterator(IEnumerable first, IEnumerable second, Func keySelector, IEqualityComparer? comparer) + { + var set = new HashSet(second, comparer); + + foreach (TSource element in first) + { + if (set.Add(keySelector(element))) + { + yield return element; + } + } + } } } diff --git a/src/libraries/System.Linq/src/System/Linq/Intersect.cs b/src/libraries/System.Linq/src/System/Linq/Intersect.cs index 88c679106aba2d..a9519a6e73a7b0 100644 --- a/src/libraries/System.Linq/src/System/Linq/Intersect.cs +++ b/src/libraries/System.Linq/src/System/Linq/Intersect.cs @@ -7,7 +7,9 @@ namespace System.Linq { public static partial class Enumerable { - public static IEnumerable Intersect(this IEnumerable first, IEnumerable second) + public static IEnumerable Intersect(this IEnumerable first, IEnumerable second) => Intersect(first, second, null); + + public static IEnumerable Intersect(this IEnumerable first, IEnumerable second, IEqualityComparer? comparer) { if (first == null) { @@ -19,22 +21,27 @@ public static IEnumerable Intersect(this IEnumerable ThrowHelper.ThrowArgumentNullException(ExceptionArgument.second); } - return IntersectIterator(first, second, null); + return IntersectIterator(first, second, comparer); } - public static IEnumerable Intersect(this IEnumerable first, IEnumerable second, IEqualityComparer? comparer) + public static IEnumerable IntersectBy(this IEnumerable first, IEnumerable second, Func keySelector) => IntersectBy(first, second, keySelector, null); + + public static IEnumerable IntersectBy(this IEnumerable first, IEnumerable second, Func keySelector, IEqualityComparer? comparer) { - if (first == null) + if (first is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.first); } - - if (second == null) + if (second is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.second); } + if (keySelector is null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.keySelector); + } - return IntersectIterator(first, second, comparer); + return IntersectByIterator(first, second, keySelector, comparer); } private static IEnumerable IntersectIterator(IEnumerable first, IEnumerable second, IEqualityComparer? comparer) @@ -49,5 +56,18 @@ private static IEnumerable IntersectIterator(IEnumerable IntersectByIterator(IEnumerable first, IEnumerable second, Func keySelector, IEqualityComparer? comparer) + { + var set = new HashSet(second, comparer); + + foreach (TSource element in first) + { + if (set.Remove(keySelector(element))) + { + yield return element; + } + } + } } } diff --git a/src/libraries/System.Linq/src/System/Linq/Max.cs b/src/libraries/System.Linq/src/System/Linq/Max.cs index c430306156400c..8e9f918d0724cb 100644 --- a/src/libraries/System.Linq/src/System/Linq/Max.cs +++ b/src/libraries/System.Linq/src/System/Linq/Max.cs @@ -441,13 +441,16 @@ public static decimal Max(this IEnumerable source) return value; } - public static TSource? Max(this IEnumerable source) + public static TSource? Max(this IEnumerable source) => Max(source, comparer: null); + public static TSource? Max(this IEnumerable source, IComparer? comparer) { if (source == null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source); } + comparer ??= Comparer.Default; + TSource? value = default; using (IEnumerator e = source.GetEnumerator()) { @@ -464,13 +467,12 @@ public static decimal Max(this IEnumerable source) } while (value == null); - Comparer comparer = Comparer.Default; while (e.MoveNext()) { - TSource x = e.Current; - if (x != null && comparer.Compare(x, value) > 0) + TSource next = e.Current; + if (next != null && comparer.Compare(next, value) > 0) { - value = x; + value = next; } } } @@ -482,12 +484,111 @@ public static decimal Max(this IEnumerable source) } value = e.Current; + if (comparer == Comparer.Default) + { + while (e.MoveNext()) + { + TSource next = e.Current; + if (Comparer.Default.Compare(next, value) > 0) + { + value = next; + } + } + } + else + { + while (e.MoveNext()) + { + TSource next = e.Current; + if (comparer.Compare(next, value) > 0) + { + value = next; + } + } + } + } + } + + return value; + } + + public static TSource? MaxBy(this IEnumerable source, Func keySelector) => MaxBy(source, keySelector, null); + public static TSource? MaxBy(this IEnumerable source, Func keySelector, IComparer? comparer) + { + if (source == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source); + } + + if (keySelector == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.keySelector); + } + + comparer ??= Comparer.Default; + + TKey? key = default; + TSource? value = default; + using (IEnumerator e = source.GetEnumerator()) + { + if (key == null) + { + do + { + if (!e.MoveNext()) + { + return value; + } + + value = e.Current; + key = keySelector(value); + } + while (key == null); + while (e.MoveNext()) { - TSource x = e.Current; - if (Comparer.Default.Compare(x, value) > 0) + TSource nextValue = e.Current; + TKey nextKey = keySelector(nextValue); + if (nextKey != null && comparer.Compare(nextKey, key) > 0) { - value = x; + key = nextKey; + value = nextValue; + } + } + } + else + { + if (!e.MoveNext()) + { + ThrowHelper.ThrowNoElementsException(); + } + + value = e.Current; + key = keySelector(value); + if (comparer == Comparer.Default) + { + while (e.MoveNext()) + { + TSource nextValue = e.Current; + TKey nextKey = keySelector(nextValue); + if (Comparer.Default.Compare(nextKey, key) > 0) + { + key = nextKey; + value = nextValue; + } + } + } + else + { + while (e.MoveNext()) + { + TSource nextValue = e.Current; + TKey nextKey = keySelector(nextValue); + if (comparer.Compare(nextKey, key) > 0) + { + key = nextKey; + value = nextValue; + } } } } diff --git a/src/libraries/System.Linq/src/System/Linq/Min.cs b/src/libraries/System.Linq/src/System/Linq/Min.cs index f9531778910a42..fb90cdf348143a 100644 --- a/src/libraries/System.Linq/src/System/Linq/Min.cs +++ b/src/libraries/System.Linq/src/System/Linq/Min.cs @@ -399,13 +399,16 @@ public static decimal Min(this IEnumerable source) return value; } - public static TSource? Min(this IEnumerable source) + public static TSource? Min(this IEnumerable source) => Min(source, comparer: null); + public static TSource? Min(this IEnumerable source, IComparer? comparer) { if (source == null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source); } + comparer ??= Comparer.Default; + TSource? value = default; using (IEnumerator e = source.GetEnumerator()) { @@ -422,13 +425,12 @@ public static decimal Min(this IEnumerable source) } while (value == null); - Comparer comparer = Comparer.Default; while (e.MoveNext()) { - TSource x = e.Current; - if (x != null && comparer.Compare(x, value) < 0) + TSource next = e.Current; + if (next != null && comparer.Compare(next, value) < 0) { - value = x; + value = next; } } } @@ -440,12 +442,111 @@ public static decimal Min(this IEnumerable source) } value = e.Current; + if (comparer == Comparer.Default) + { + while (e.MoveNext()) + { + TSource next = e.Current; + if (Comparer.Default.Compare(next, value) < 0) + { + value = next; + } + } + } + else + { + while (e.MoveNext()) + { + TSource next = e.Current; + if (comparer.Compare(next, value) < 0) + { + value = next; + } + } + } + } + } + + return value; + } + + public static TSource? MinBy(this IEnumerable source, Func keySelector) => MinBy(source, keySelector, comparer: null); + public static TSource? MinBy(this IEnumerable source, Func keySelector, IComparer? comparer) + { + if (source == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source); + } + + if (keySelector == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.keySelector); + } + + comparer ??= Comparer.Default; + + TKey? key = default; + TSource? value = default; + using (IEnumerator e = source.GetEnumerator()) + { + if (key == null) + { + do + { + if (!e.MoveNext()) + { + return value; + } + + value = e.Current; + key = keySelector(value); + } + while (key == null); + while (e.MoveNext()) { - TSource x = e.Current; - if (Comparer.Default.Compare(x, value) < 0) + TSource nextValue = e.Current; + TKey nextKey = keySelector(nextValue); + if (nextKey != null && comparer.Compare(nextKey, key) < 0) { - value = x; + key = nextKey; + value = nextValue; + } + } + } + else + { + if (!e.MoveNext()) + { + ThrowHelper.ThrowNoElementsException(); + } + + value = e.Current; + key = keySelector(value); + if (comparer == Comparer.Default) + { + while (e.MoveNext()) + { + TSource nextValue = e.Current; + TKey nextKey = keySelector(nextValue); + if (Comparer.Default.Compare(nextKey, key) < 0) + { + key = nextKey; + value = nextValue; + } + } + } + else + { + while (e.MoveNext()) + { + TSource nextValue = e.Current; + TKey nextKey = keySelector(nextValue); + if (comparer.Compare(nextKey, key) < 0) + { + key = nextKey; + value = nextValue; + } } } } diff --git a/src/libraries/System.Linq/src/System/Linq/Union.cs b/src/libraries/System.Linq/src/System/Linq/Union.cs index d9b3c4bdb065b7..bc7eb36843d721 100644 --- a/src/libraries/System.Linq/src/System/Linq/Union.cs +++ b/src/libraries/System.Linq/src/System/Linq/Union.cs @@ -26,6 +26,47 @@ public static IEnumerable Union(this IEnumerable firs return first is UnionIterator union && AreEqualityComparersEqual(comparer, union._comparer) ? union.Union(second) : new UnionIterator2(first, second, comparer); } + public static IEnumerable UnionBy(this IEnumerable first, IEnumerable second, Func keySelector) => UnionBy(first, second, keySelector, null); + + public static IEnumerable UnionBy(this IEnumerable first, IEnumerable second, Func keySelector, IEqualityComparer? comparer) + { + if (first is null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.first); + } + if (second is null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.second); + } + if (keySelector is null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.keySelector); + } + + return UnionByIterator(first, second, keySelector, comparer); + } + + private static IEnumerable UnionByIterator(IEnumerable first, IEnumerable second, Func keySelector, IEqualityComparer? comparer) + { + var set = new HashSet(DefaultInternalSetCapacity, comparer); + + foreach (TSource element in first) + { + if (set.Add(keySelector(element))) + { + yield return element; + } + } + + foreach (TSource element in second) + { + if (set.Add(keySelector(element))) + { + yield return element; + } + } + } + /// /// An iterator that yields distinct values from two or more . /// diff --git a/src/libraries/System.Linq/tests/DistinctTests.cs b/src/libraries/System.Linq/tests/DistinctTests.cs index a5a438d5dcb83c..7408e96ddb38ce 100644 --- a/src/libraries/System.Linq/tests/DistinctTests.cs +++ b/src/libraries/System.Linq/tests/DistinctTests.cs @@ -267,5 +267,104 @@ public void RepeatEnumerating() Assert.Equal(result, result); } + + [Fact] + public void DistinctBy_SourceNull_ThrowsArgumentNullException() + { + string[] first = null; + + AssertExtensions.Throws("source", () => first.DistinctBy(x => x)); + AssertExtensions.Throws("source", () => first.DistinctBy(x => x, new AnagramEqualityComparer())); + } + + [Fact] + public void DistinctBy_KeySelectorNull_ThrowsArgumentNullException() + { + string[] source = { "Bob", "Tim", "Robert", "Chris" }; + Func keySelector = null; + + AssertExtensions.Throws("keySelector", () => source.DistinctBy(keySelector)); + AssertExtensions.Throws("keySelector", () => source.DistinctBy(keySelector, new AnagramEqualityComparer())); + } + + [Theory] + [MemberData(nameof(DistinctBy_TestData))] + public static void DistinctBy_HasExpectedOutput(IEnumerable source, Func keySelector, IEqualityComparer? comparer, IEnumerable expected) + { + Assert.Equal(expected, source.DistinctBy(keySelector, comparer)); + } + + [Theory] + [MemberData(nameof(DistinctBy_TestData))] + public static void DistinctBy_RunOnce_HasExpectedOutput(IEnumerable source, Func keySelector, IEqualityComparer? comparer, IEnumerable expected) + { + Assert.Equal(expected, source.RunOnce().DistinctBy(keySelector, comparer)); + } + + public static IEnumerable DistinctBy_TestData() + { + yield return WrapArgs( + source: Enumerable.Range(0, 10), + keySelector: x => x, + comparer: null, + expected: Enumerable.Range(0, 10)); + + yield return WrapArgs( + source: Enumerable.Range(5, 10), + keySelector: x => true, + comparer: null, + expected: new int[] { 5 }); + + yield return WrapArgs( + source: Enumerable.Range(0, 20), + keySelector: x => x % 5, + comparer: null, + expected: Enumerable.Range(0, 5)); + + yield return WrapArgs( + source: Enumerable.Repeat(5, 20), + keySelector: x => x, + comparer: null, + expected: Enumerable.Repeat(5, 1)); + + yield return WrapArgs( + source: new string[] { "Bob", "bob", "tim", "Bob", "Tim" }, + keySelector: x => x, + null, + expected: new string[] { "Bob", "bob", "tim", "Tim" }); + + yield return WrapArgs( + source: new string[] { "Bob", "bob", "tim", "Bob", "Tim" }, + keySelector: x => x, + StringComparer.OrdinalIgnoreCase, + expected: new string[] { "Bob", "tim" }); + + yield return WrapArgs( + source: new (string Name, int Age)[] { ("Tom", 20), ("Dick", 30), ("Harry", 40) }, + keySelector: x => x.Age, + comparer: null, + expected: new (string Name, int Age)[] { ("Tom", 20), ("Dick", 30), ("Harry", 40) }); + + yield return WrapArgs( + source: new (string Name, int Age)[] { ("Tom", 20), ("Dick", 20), ("Harry", 40) }, + keySelector: x => x.Age, + comparer: null, + expected: new (string Name, int Age)[] { ("Tom", 20), ("Harry", 40) }); + + yield return WrapArgs( + source: new (string Name, int Age)[] { ("Bob", 20), ("bob", 30), ("Harry", 40) }, + keySelector: x => x.Name, + comparer: null, + expected: new (string Name, int Age)[] { ("Bob", 20), ("bob", 30), ("Harry", 40) }); + + yield return WrapArgs( + source: new (string Name, int Age)[] { ("Bob", 20), ("bob", 30), ("Harry", 40) }, + keySelector: x => x.Name, + comparer: StringComparer.OrdinalIgnoreCase, + expected: new (string Name, int Age)[] { ("Bob", 20), ("Harry", 40) }); + + object[] WrapArgs(IEnumerable source, Func keySelector, IEqualityComparer? comparer, IEnumerable expected) + => new object[] { source, keySelector, comparer, expected }; + } } } diff --git a/src/libraries/System.Linq/tests/ExceptTests.cs b/src/libraries/System.Linq/tests/ExceptTests.cs index 5cb24615ca356f..36976b7cc57a54 100644 --- a/src/libraries/System.Linq/tests/ExceptTests.cs +++ b/src/libraries/System.Linq/tests/ExceptTests.cs @@ -27,9 +27,6 @@ public void SameResultsRepeatCallsStringQuery() var q2 = from x2 in new[] { "!@#$%^", "C", "AAA", "", "Calling Twice", "SoS" } select x2; - var rst1 = q1.Except(q2); - var rst2 = q1.Except(q2); - Assert.Equal(q1.Except(q2), q1.Except(q2)); } @@ -141,5 +138,112 @@ public void HashSetWithBuiltInComparer_HashSetContainsNotUsed() Assert.Equal(new[] { "A" }, input2.Except(input1, EqualityComparer.Default)); Assert.Equal(Enumerable.Empty(), input2.Except(input1, StringComparer.OrdinalIgnoreCase)); } + + [Fact] + public void ExceptBy_FirstNull_ThrowsArgumentNullException() + { + string[] first = null; + string[] second = { "bBo", "shriC" }; + + AssertExtensions.Throws("first", () => first.ExceptBy(second, x => x)); + AssertExtensions.Throws("first", () => first.ExceptBy(second, x => x, new AnagramEqualityComparer())); + } + + [Fact] + public void ExceptBy_SecondNull_ThrowsArgumentNullException() + { + string[] first = { "Bob", "Tim", "Robert", "Chris" }; + string[] second = null; + + AssertExtensions.Throws("second", () => first.ExceptBy(second, x => x)); + AssertExtensions.Throws("second", () => first.ExceptBy(second, x => x, new AnagramEqualityComparer())); + } + + [Fact] + public void ExceptBy_KeySelectorNull_ThrowsArgumentNullException() + { + string[] first = { "Bob", "Tim", "Robert", "Chris" }; + string[] second = { "bBo", "shriC" }; + Func keySelector = null; + + AssertExtensions.Throws("keySelector", () => first.ExceptBy(second, keySelector)); + AssertExtensions.Throws("keySelector", () => first.ExceptBy(second, keySelector, new AnagramEqualityComparer())); + } + + [Theory] + [MemberData(nameof(ExceptBy_TestData))] + public static void ExceptBy_HasExpectedOutput(IEnumerable first, IEnumerable second, Func keySelector, IEqualityComparer? comparer, IEnumerable expected) + { + Assert.Equal(expected, first.ExceptBy(second, keySelector, comparer)); + } + + [Theory] + [MemberData(nameof(ExceptBy_TestData))] + public static void ExceptBy_RunOnce_HasExpectedOutput(IEnumerable first, IEnumerable second, Func keySelector, IEqualityComparer? comparer, IEnumerable expected) + { + Assert.Equal(expected, first.RunOnce().ExceptBy(second.RunOnce(), keySelector, comparer)); + } + + public static IEnumerable ExceptBy_TestData() + { + yield return WrapArgs( + first: Enumerable.Range(0, 10), + second: Enumerable.Range(0, 5), + keySelector: x => x, + comparer: null, + expected: Enumerable.Range(5, 5)); + + yield return WrapArgs( + first: Enumerable.Repeat(5, 20), + second: Enumerable.Empty(), + keySelector: x => x, + comparer: null, + expected: Enumerable.Repeat(5, 1)); + + yield return WrapArgs( + first: Enumerable.Repeat(5, 20), + second: Enumerable.Repeat(5, 3), + keySelector: x => x, + comparer: null, + expected: Enumerable.Empty()); + + yield return WrapArgs( + first: new string[] { "Bob", "Tim", "Robert", "Chris" }, + second: new string[] { "bBo", "shriC" }, + keySelector: x => x, + null, + expected: new string[] { "Bob", "Tim", "Robert", "Chris" }); + + yield return WrapArgs( + first: new string[] { "Bob", "Tim", "Robert", "Chris" }, + second: new string[] { "bBo", "shriC" }, + keySelector: x => x, + new AnagramEqualityComparer(), + expected: new string[] { "Tim", "Robert" }); + + yield return WrapArgs( + first: new (string Name, int Age)[] { ("Tom", 20), ("Dick", 30), ("Harry", 40) }, + second: new int[] { 15, 20, 40 }, + keySelector: x => x.Age, + comparer: null, + expected: new (string Name, int Age)[] { ("Dick", 30) }); + + yield return WrapArgs( + first: new (string Name, int Age)[] { ("Tom", 20), ("Dick", 30), ("Harry", 40) }, + second: new string[] { "moT" }, + keySelector: x => x.Name, + comparer: null, + expected: new (string Name, int Age)[] { ("Tom", 20), ("Dick", 30), ("Harry", 40) }); + + yield return WrapArgs( + first: new (string Name, int Age)[] { ("Tom", 20), ("Dick", 30), ("Harry", 40) }, + second: new string[] { "moT" }, + keySelector: x => x.Name, + comparer: new AnagramEqualityComparer(), + expected: new (string Name, int Age)[] { ("Dick", 30), ("Harry", 40) }); + + object[] WrapArgs(IEnumerable first, IEnumerable second, Func keySelector, IEqualityComparer? comparer, IEnumerable expected) + => new object[] { first, second, keySelector, comparer, expected }; + } } } diff --git a/src/libraries/System.Linq/tests/IntersectTests.cs b/src/libraries/System.Linq/tests/IntersectTests.cs index 361854b69c170c..7b1d5fdfbd873d 100644 --- a/src/libraries/System.Linq/tests/IntersectTests.cs +++ b/src/libraries/System.Linq/tests/IntersectTests.cs @@ -135,5 +135,119 @@ public void HashSetWithBuiltInComparer_HashSetContainsNotUsed() Assert.Equal(Enumerable.Empty(), input2.Intersect(input1, EqualityComparer.Default)); Assert.Equal(new[] { "A" }, input2.Intersect(input1, StringComparer.OrdinalIgnoreCase)); } + + [Fact] + public void IntersectBy_FirstNull_ThrowsArgumentNullException() + { + string[] first = null; + string[] second = { "bBo", "shriC" }; + + AssertExtensions.Throws("first", () => first.IntersectBy(second, x => x)); + AssertExtensions.Throws("first", () => first.IntersectBy(second, x => x, new AnagramEqualityComparer())); + } + + [Fact] + public void IntersectBy_SecondNull_ThrowsArgumentNullException() + { + string[] first = { "Bob", "Tim", "Robert", "Chris" }; + string[] second = null; + + AssertExtensions.Throws("second", () => first.IntersectBy(second, x => x)); + AssertExtensions.Throws("second", () => first.IntersectBy(second, x => x, new AnagramEqualityComparer())); + } + + [Fact] + public void IntersectBy_KeySelectorNull_ThrowsArgumentNullException() + { + string[] first = { "Bob", "Tim", "Robert", "Chris" }; + string[] second = { "bBo", "shriC" }; + Func keySelector = null; + + AssertExtensions.Throws("keySelector", () => first.IntersectBy(second, keySelector)); + AssertExtensions.Throws("keySelector", () => first.IntersectBy(second, keySelector, new AnagramEqualityComparer())); + } + + [Theory] + [MemberData(nameof(IntersectBy_TestData))] + public static void IntersectBy_HasExpectedOutput(IEnumerable first, IEnumerable second, Func keySelector, IEqualityComparer? comparer, IEnumerable expected) + { + Assert.Equal(expected, first.IntersectBy(second, keySelector, comparer)); + } + + [Theory] + [MemberData(nameof(IntersectBy_TestData))] + public static void IntersectBy_RunOnce_HasExpectedOutput(IEnumerable first, IEnumerable second, Func keySelector, IEqualityComparer? comparer, IEnumerable expected) + { + Assert.Equal(expected, first.RunOnce().IntersectBy(second.RunOnce(), keySelector, comparer)); + } + + public static IEnumerable IntersectBy_TestData() + { + yield return WrapArgs( + first: Enumerable.Range(0, 10), + second: Enumerable.Range(0, 5), + keySelector: x => x, + comparer: null, + expected: Enumerable.Range(0, 5)); + + yield return WrapArgs( + first: Enumerable.Range(0, 10), + second: Enumerable.Range(10, 10), + keySelector: x => x, + comparer: null, + expected: Enumerable.Empty()); + + yield return WrapArgs( + first: Enumerable.Repeat(5, 20), + second: Enumerable.Empty(), + keySelector: x => x, + comparer: null, + expected: Enumerable.Empty()); + + yield return WrapArgs( + first: Enumerable.Repeat(5, 20), + second: Enumerable.Repeat(5, 3), + keySelector: x => x, + comparer: null, + expected: Enumerable.Repeat(5, 1)); + + yield return WrapArgs( + first: new string[] { "Bob", "Tim", "Robert", "Chris" }, + second: new string[] { "bBo", "shriC" }, + keySelector: x => x, + null, + expected: Array.Empty()); + + yield return WrapArgs( + first: new string[] { "Bob", "Tim", "Robert", "Chris" }, + second: new string[] { "bBo", "shriC" }, + keySelector: x => x, + new AnagramEqualityComparer(), + expected: new string[] { "Bob", "Chris" }); + + yield return WrapArgs( + first: new (string Name, int Age)[] { ("Tom", 20), ("Dick", 30), ("Harry", 40) }, + second: new int[] { 15, 20, 40 }, + keySelector: x => x.Age, + comparer: null, + expected: new (string Name, int Age)[] { ("Tom", 20), ("Harry", 40) }); + + yield return WrapArgs( + first: new (string Name, int Age)[] { ("Tom", 20), ("Dick", 30), ("Harry", 40) }, + second: new string[] { "moT" }, + keySelector: x => x.Name, + comparer: null, + expected: Array.Empty<(string Name, int Age)>()); + + yield return WrapArgs( + first: new (string Name, int Age)[] { ("Tom", 20), ("Dick", 30), ("Harry", 40) }, + second: new string[] { "moT" }, + keySelector: x => x.Name, + comparer: new AnagramEqualityComparer(), + expected: new (string Name, int Age)[] { ("Tom", 20) }); + + object[] WrapArgs(IEnumerable first, IEnumerable second, Func keySelector, IEqualityComparer? comparer, IEnumerable expected) + => new object[] { first, second, keySelector, comparer, expected }; + } } } diff --git a/src/libraries/System.Linq/tests/MaxTests.cs b/src/libraries/System.Linq/tests/MaxTests.cs index 6c1e56a62df1e5..ed66912a8c2cc2 100644 --- a/src/libraries/System.Linq/tests/MaxTests.cs +++ b/src/libraries/System.Linq/tests/MaxTests.cs @@ -770,5 +770,193 @@ public void Max_Boolean_EmptySource_ThrowsInvalidOperationException() { Assert.Throws(() => Enumerable.Empty().Max()); } + + [Fact] + public static void Max_Generic_NullSource_ThrowsArgumentNullException() + { + IEnumerable source = null; + + AssertExtensions.Throws("source", () => source.Max()); + AssertExtensions.Throws("source", () => source.Max(comparer: null)); + AssertExtensions.Throws("source", () => source.Max(Comparer.Create((_, _) => 0))); + } + + [Fact] + public static void Max_Generic_EmptyStructSource_ThrowsInvalidOperationException() + { + Assert.Throws(() => Enumerable.Empty().Max()); + Assert.Throws(() => Enumerable.Empty().Max(comparer: null)); + Assert.Throws(() => Enumerable.Empty().Max(Comparer.Create((_,_) => 0))); + } + + [Theory] + [MemberData(nameof(Max_Generic_TestData))] + public static void Max_Generic_HasExpectedOutput(IEnumerable source, IComparer? comparer, TSource? expected) + { + Assert.Equal(expected, source.Max(comparer)); + } + + [Theory] + [MemberData(nameof(Max_Generic_TestData))] + public static void Max_Generic_RunOnce_HasExpectedOutput(IEnumerable source, IComparer? comparer, TSource? expected) + { + Assert.Equal(expected, source.RunOnce().Max(comparer)); + } + + public static IEnumerable Max_Generic_TestData() + { + yield return WrapArgs( + source: Enumerable.Empty(), + comparer: null, + expected: null); + + yield return WrapArgs( + source: Enumerable.Empty(), + comparer: Comparer.Create((_,_) => 0), + expected: null); + + yield return WrapArgs( + source: Enumerable.Range(0, 10), + comparer: null, + expected: 9); + + yield return WrapArgs( + source: Enumerable.Range(0, 10), + comparer: Comparer.Create((x, y) => -x.CompareTo(y)), + expected: 0); + + yield return WrapArgs( + source: Enumerable.Range(0, 10), + comparer: Comparer.Create((x,y) => 0), + expected: 0); + + yield return WrapArgs( + source: new string[] { "Aardvark", "Zyzzyva", "Zebra", "Antelope" }, + comparer: null, + expected: "Zyzzyva"); + + yield return WrapArgs( + source: new string[] { "Aardvark", "Zyzzyva", "Zebra", "Antelope" }, + comparer: Comparer.Create((x, y) => -x.CompareTo(y)), + expected: "Aardvark"); + + object[] WrapArgs(IEnumerable source, IComparer? comparer, TSource? expected) + => new object[] { source, comparer, expected }; + } + + [Fact] + public static void MaxBy_Generic_NullSource_ThrowsArgumentNullException() + { + IEnumerable source = null; + + AssertExtensions.Throws("source", () => source.MaxBy(x => x)); + AssertExtensions.Throws("source", () => source.MaxBy(x => x, comparer: null)); + AssertExtensions.Throws("source", () => source.MaxBy(x => x, Comparer.Create((_, _) => 0))); + } + + [Fact] + public static void MaxBy_Generic_NullKeySelector_ThrowsArgumentNullException() + { + IEnumerable source = Enumerable.Empty(); + Func keySelector = null; + + AssertExtensions.Throws("keySelector", () => source.MaxBy(keySelector)); + AssertExtensions.Throws("keySelector", () => source.MaxBy(keySelector, comparer: null)); + AssertExtensions.Throws("keySelector", () => source.MaxBy(keySelector, Comparer.Create((_, _) => 0))); + } + + [Fact] + public static void MaxBy_Generic_EmptyStructSource_ThrowsInvalidOperationException() + { + Assert.Throws(() => Enumerable.Empty().MaxBy(x => x)); + Assert.Throws(() => Enumerable.Empty().MaxBy(x => x, comparer: null)); + Assert.Throws(() => Enumerable.Empty().MaxBy(x => x, Comparer.Create((_, _) => 0))); + } + + [Theory] + [MemberData(nameof(MaxBy_Generic_TestData))] + public static void MaxBy_Generic_HasExpectedOutput(IEnumerable source, Func keySelector, IComparer? comparer, TSource? expected) + { + Assert.Equal(expected, source.MaxBy(keySelector, comparer)); + } + + [Theory] + [MemberData(nameof(MaxBy_Generic_TestData))] + public static void MaxBy_Generic_RunOnce_HasExpectedOutput(IEnumerable source, Func keySelector, IComparer? comparer, TSource? expected) + { + Assert.Equal(expected, source.RunOnce().MaxBy(keySelector, comparer)); + } + + public static IEnumerable MaxBy_Generic_TestData() + { + yield return WrapArgs( + source: Enumerable.Empty(), + keySelector: x => x, + comparer: null, + expected: null); + + yield return WrapArgs( + source: Enumerable.Empty(), + keySelector: x => x, + comparer: Comparer.Create((_, _) => 0), + expected: null); + + yield return WrapArgs( + source: Enumerable.Range(0, 10), + keySelector: x => x, + comparer: null, + expected: 9); + + yield return WrapArgs( + source: Enumerable.Range(0, 10), + keySelector: x => x, + comparer: Comparer.Create((x, y) => -x.CompareTo(y)), + expected: 0); + + yield return WrapArgs( + source: Enumerable.Range(0, 10), + keySelector: x => x, + comparer: Comparer.Create((x, y) => 0), + expected: 0); + + yield return WrapArgs( + source: new string[] { "Aardvark", "Zyzzyva", "Zebra", "Antelope" }, + keySelector: x => x, + comparer: null, + expected: "Zyzzyva"); + + yield return WrapArgs( + source: new string[] { "Aardvark", "Zyzzyva", "Zebra", "Antelope" }, + keySelector: x => x, + comparer: Comparer.Create((x, y) => -x.CompareTo(y)), + expected: "Aardvark"); + + yield return WrapArgs( + source: new (string Name, int Age) [] { ("Tom", 43), ("Dick", 55), ("Harry", 20) }, + keySelector: x => x.Age, + comparer: null, + expected: (Name: "Dick", Age: 55)); + + yield return WrapArgs( + source: new (string Name, int Age)[] { ("Tom", 43), ("Dick", 55), ("Harry", 20) }, + keySelector: x => x.Age, + comparer: Comparer.Create((x, y) => -x.CompareTo(y)), + expected: (Name: "Harry", Age: 20)); + + yield return WrapArgs( + source: new (string Name, int Age)[] { ("Tom", 43), ("Dick", 55), ("Harry", 20) }, + keySelector: x => x.Name, + comparer: null, + expected: (Name: "Tom", Age: 43)); + + yield return WrapArgs( + source: new (string Name, int Age)[] { ("Tom", 43), ("Dick", 55), ("Harry", 20) }, + keySelector: x => x.Name, + comparer: Comparer.Create((x, y) => -x.CompareTo(y)), + expected: (Name: "Dick", Age: 55)); + + object[] WrapArgs(IEnumerable source, Func keySelector, IComparer? comparer, TSource? expected) + => new object[] { source, keySelector, comparer, expected }; + } } } diff --git a/src/libraries/System.Linq/tests/MinTests.cs b/src/libraries/System.Linq/tests/MinTests.cs index 44a9b1a1e4d97d..3d6eef37d09000 100644 --- a/src/libraries/System.Linq/tests/MinTests.cs +++ b/src/libraries/System.Linq/tests/MinTests.cs @@ -748,5 +748,193 @@ public void Min_Bool_EmptySource_ThrowsInvalodOperationException() { Assert.Throws(() => Enumerable.Empty().Min()); } + + [Fact] + public static void Min_Generic_NullSource_ThrowsArgumentNullException() + { + IEnumerable source = null; + + AssertExtensions.Throws("source", () => source.Min()); + AssertExtensions.Throws("source", () => source.Min(comparer: null)); + AssertExtensions.Throws("source", () => source.Min(Comparer.Create((_, _) => 0))); + } + + [Fact] + public static void Min_Generic_EmptyStructSource_ThrowsInvalidOperationException() + { + Assert.Throws(() => Enumerable.Empty().Min()); + Assert.Throws(() => Enumerable.Empty().Min(comparer: null)); + Assert.Throws(() => Enumerable.Empty().Min(Comparer.Create((_, _) => 0))); + } + + [Theory] + [MemberData(nameof(Min_Generic_TestData))] + public static void Min_Generic_HasExpectedOutput(IEnumerable source, IComparer? comparer, TSource? expected) + { + Assert.Equal(expected, source.Min(comparer)); + } + + [Theory] + [MemberData(nameof(Min_Generic_TestData))] + public static void Min_Generic_RunOnce_HasExpectedOutput(IEnumerable source, IComparer? comparer, TSource? expected) + { + Assert.Equal(expected, source.RunOnce().Min(comparer)); + } + + public static IEnumerable Min_Generic_TestData() + { + yield return WrapArgs( + source: Enumerable.Empty(), + comparer: null, + expected: null); + + yield return WrapArgs( + source: Enumerable.Empty(), + comparer: Comparer.Create((_, _) => 0), + expected: null); + + yield return WrapArgs( + source: Enumerable.Range(0, 10), + comparer: null, + expected: 0); + + yield return WrapArgs( + source: Enumerable.Range(0, 10), + comparer: Comparer.Create((x, y) => -x.CompareTo(y)), + expected: 9); + + yield return WrapArgs( + source: Enumerable.Range(0, 10), + comparer: Comparer.Create((x, y) => 0), + expected: 0); + + yield return WrapArgs( + source: new string[] { "Aardvark", "Zyzzyva", "Zebra", "Antelope" }, + comparer: null, + expected: "Aardvark"); + + yield return WrapArgs( + source: new string[] { "Aardvark", "Zyzzyva", "Zebra", "Antelope" }, + comparer: Comparer.Create((x, y) => -x.CompareTo(y)), + expected: "Zyzzyva"); + + object[] WrapArgs(IEnumerable source, IComparer? comparer, TSource? expected) + => new object[] { source, comparer, expected }; + } + + [Fact] + public static void MinBy_Generic_NullSource_ThrowsArgumentNullException() + { + IEnumerable source = null; + + AssertExtensions.Throws("source", () => source.MinBy(x => x)); + AssertExtensions.Throws("source", () => source.MinBy(x => x, comparer: null)); + AssertExtensions.Throws("source", () => source.MinBy(x => x, Comparer.Create((_, _) => 0))); + } + + [Fact] + public static void MinBy_Generic_NullKeySelector_ThrowsArgumentNullException() + { + IEnumerable source = Enumerable.Empty(); + Func keySelector = null; + + AssertExtensions.Throws("keySelector", () => source.MinBy(keySelector)); + AssertExtensions.Throws("keySelector", () => source.MinBy(keySelector, comparer: null)); + AssertExtensions.Throws("keySelector", () => source.MinBy(keySelector, Comparer.Create((_, _) => 0))); + } + + [Fact] + public static void MinBy_Generic_EmptyStructSource_ThrowsInvalidOperationException() + { + Assert.Throws(() => Enumerable.Empty().MinBy(x => x)); + Assert.Throws(() => Enumerable.Empty().MinBy(x => x, comparer: null)); + Assert.Throws(() => Enumerable.Empty().MinBy(x => x, Comparer.Create((_, _) => 0))); + } + + [Theory] + [MemberData(nameof(MinBy_Generic_TestData))] + public static void MinBy_Generic_HasExpectedOutput(IEnumerable source, Func keySelector, IComparer? comparer, TSource? expected) + { + Assert.Equal(expected, source.MinBy(keySelector, comparer)); + } + + [Theory] + [MemberData(nameof(MinBy_Generic_TestData))] + public static void MinBy_Generic_RunOnce_HasExpectedOutput(IEnumerable source, Func keySelector, IComparer? comparer, TSource? expected) + { + Assert.Equal(expected, source.RunOnce().MinBy(keySelector, comparer)); + } + + public static IEnumerable MinBy_Generic_TestData() + { + yield return WrapArgs( + source: Enumerable.Empty(), + keySelector: x => x, + comparer: null, + expected: null); + + yield return WrapArgs( + source: Enumerable.Empty(), + keySelector: x => x, + comparer: Comparer.Create((_, _) => 0), + expected: null); + + yield return WrapArgs( + source: Enumerable.Range(0, 10), + keySelector: x => x, + comparer: null, + expected: 0); + + yield return WrapArgs( + source: Enumerable.Range(0, 10), + keySelector: x => x, + comparer: Comparer.Create((x, y) => -x.CompareTo(y)), + expected: 9); + + yield return WrapArgs( + source: Enumerable.Range(0, 10), + keySelector: x => x, + comparer: Comparer.Create((x, y) => 0), + expected: 0); + + yield return WrapArgs( + source: new string[] { "Aardvark", "Zyzzyva", "Zebra", "Antelope" }, + keySelector: x => x, + comparer: null, + expected: "Aardvark"); + + yield return WrapArgs( + source: new string[] { "Aardvark", "Zyzzyva", "Zebra", "Antelope" }, + keySelector: x => x, + comparer: Comparer.Create((x, y) => -x.CompareTo(y)), + expected: "Zyzzyva"); + + yield return WrapArgs( + source: new (string Name, int Age)[] { ("Tom", 43), ("Dick", 55), ("Harry", 20) }, + keySelector: x => x.Age, + comparer: null, + expected: (Name: "Harry", Age: 20)); + + yield return WrapArgs( + source: new (string Name, int Age)[] { ("Tom", 43), ("Dick", 55), ("Harry", 20) }, + keySelector: x => x.Age, + comparer: Comparer.Create((x, y) => -x.CompareTo(y)), + expected: (Name: "Dick", Age: 55)); + + yield return WrapArgs( + source: new (string Name, int Age)[] { ("Tom", 43), ("Dick", 55), ("Harry", 20) }, + keySelector: x => x.Name, + comparer: null, + expected: (Name: "Dick", Age: 55)); + + yield return WrapArgs( + source: new (string Name, int Age)[] { ("Tom", 43), ("Dick", 55), ("Harry", 20) }, + keySelector: x => x.Name, + comparer: Comparer.Create((x, y) => -x.CompareTo(y)), + expected: (Name: "Tom", Age: 43)); + + object[] WrapArgs(IEnumerable source, Func keySelector, IComparer? comparer, TSource? expected) + => new object[] { source, keySelector, comparer, expected }; + } } } diff --git a/src/libraries/System.Linq/tests/UnionTests.cs b/src/libraries/System.Linq/tests/UnionTests.cs index 9c95220e37800d..9dbfd822b16395 100644 --- a/src/libraries/System.Linq/tests/UnionTests.cs +++ b/src/libraries/System.Linq/tests/UnionTests.cs @@ -413,5 +413,126 @@ public void HashSetWithBuiltInComparer_HashSetContainsNotUsed() Assert.Equal(new[] { "A", "a" }, input2.Union(input1, EqualityComparer.Default)); Assert.Equal(new[] { "A" }, input2.Union(input1, StringComparer.OrdinalIgnoreCase)); } + + [Fact] + public void UnionBy_FirstNull_ThrowsArgumentNullException() + { + string[] first = null; + string[] second = { "bBo", "shriC" }; + + AssertExtensions.Throws("first", () => first.UnionBy(second, x => x)); + AssertExtensions.Throws("first", () => first.UnionBy(second, x => x, new AnagramEqualityComparer())); + } + + [Fact] + public void UnionBy_SecondNull_ThrowsArgumentNullException() + { + string[] first = { "Bob", "Tim", "Robert", "Chris" }; + string[] second = null; + + AssertExtensions.Throws("second", () => first.UnionBy(second, x => x)); + AssertExtensions.Throws("second", () => first.UnionBy(second, x => x, new AnagramEqualityComparer())); + } + + [Fact] + public void UnionBy_KeySelectorNull_ThrowsArgumentNullException() + { + string[] first = { "Bob", "Tim", "Robert", "Chris" }; + string[] second = { "bBo", "shriC" }; + Func keySelector = null; + + AssertExtensions.Throws("keySelector", () => first.UnionBy(second, keySelector)); + AssertExtensions.Throws("keySelector", () => first.UnionBy(second, keySelector, new AnagramEqualityComparer())); + } + + [Theory] + [MemberData(nameof(UnionBy_TestData))] + public static void UnionBy_HasExpectedOutput(IEnumerable first, IEnumerable second, Func keySelector, IEqualityComparer? comparer, IEnumerable expected) + { + Assert.Equal(expected, first.UnionBy(second, keySelector, comparer)); + } + + [Theory] + [MemberData(nameof(UnionBy_TestData))] + public static void UnionBy_RunOnce_HasExpectedOutput(IEnumerable first, IEnumerable second, Func keySelector, IEqualityComparer? comparer, IEnumerable expected) + { + Assert.Equal(expected, first.RunOnce().UnionBy(second.RunOnce(), keySelector, comparer)); + } + + public static IEnumerable UnionBy_TestData() + { + yield return WrapArgs( + first: Enumerable.Range(0, 7), + second: Enumerable.Range(3, 7), + keySelector: x => x, + comparer: null, + expected: Enumerable.Range(0, 10)); + + yield return WrapArgs( + first: Enumerable.Range(0, 10), + second: Enumerable.Range(10, 10), + keySelector: x => x, + comparer: null, + expected: Enumerable.Range(0, 20)); + + yield return WrapArgs( + first: Enumerable.Empty(), + second: Enumerable.Range(0, 5), + keySelector: x => x, + comparer: null, + expected: Enumerable.Range(0, 5)); + + yield return WrapArgs( + first: Enumerable.Repeat(5, 20), + second: Enumerable.Empty(), + keySelector: x => x, + comparer: null, + expected: Enumerable.Repeat(5, 1)); + + yield return WrapArgs( + first: Enumerable.Repeat(5, 20), + second: Enumerable.Repeat(5, 3), + keySelector: x => x, + comparer: null, + expected: Enumerable.Repeat(5, 1)); + + yield return WrapArgs( + first: new string[] { "Bob", "Tim", "Robert", "Chris" }, + second: new string[] { "bBo", "shriC" }, + keySelector: x => x, + null, + expected: new string[] { "Bob", "Tim", "Robert", "Chris", "bBo", "shriC" }); + + yield return WrapArgs( + first: new string[] { "Bob", "Tim", "Robert", "Chris" }, + second: new string[] { "bBo", "shriC" }, + keySelector: x => x, + new AnagramEqualityComparer(), + expected: new string[] { "Bob", "Tim", "Robert", "Chris" }); + + yield return WrapArgs( + first: new (string Name, int Age)[] { ("Tom", 20), ("Dick", 30), ("Harry", 40), ("Martin", 20) }, + second: new (string Name, int Age)[] { ("Peter", 21), ("John", 30), ("Toby", 33) }, + keySelector: x => x.Age, + comparer: null, + expected: new (string Name, int Age)[] { ("Tom", 20), ("Dick", 30), ("Harry", 40), ("Peter", 21), ("Toby", 33) }); + + yield return WrapArgs( + first: new (string Name, int Age)[] { ("Tom", 20), ("Dick", 30), ("Harry", 40), ("Martin", 20) }, + second: new (string Name, int Age)[] { ("Toby", 33), ("Harry", 35), ("tom", 67) }, + keySelector: x => x.Name, + comparer: null, + expected: new (string Name, int Age)[] { ("Tom", 20), ("Dick", 30), ("Harry", 40), ("Martin", 20), ("Toby", 33), ("tom", 67) }); + + yield return WrapArgs( + first: new (string Name, int Age)[] { ("Tom", 20), ("Dick", 30), ("Harry", 40), ("Martin", 20) }, + second: new (string Name, int Age)[] { ("Toby", 33), ("Harry", 35), ("tom", 67) }, + keySelector: x => x.Name, + comparer: StringComparer.OrdinalIgnoreCase, + expected: new (string Name, int Age)[] { ("Tom", 20), ("Dick", 30), ("Harry", 40), ("Martin", 20), ("Toby", 33) }); + + object[] WrapArgs(IEnumerable first, IEnumerable second, Func keySelector, IEqualityComparer? comparer, IEnumerable expected) + => new object[] { first, second, keySelector, comparer, expected }; + } } } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs index aa4f860cc48b9e..de2481eec6e1d0 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs @@ -105,13 +105,12 @@ internal sealed class HttpConnectionPool : IDisposable /// The port with which this pool is associated. /// The SSL host with which this pool is associated. /// The proxy this pool targets (optional). - /// The maximum number of connections allowed to be associated with the pool at any given time. - public HttpConnectionPool(HttpConnectionPoolManager poolManager, HttpConnectionKind kind, string? host, int port, string? sslHostName, Uri? proxyUri, int maxConnections) + public HttpConnectionPool(HttpConnectionPoolManager poolManager, HttpConnectionKind kind, string? host, int port, string? sslHostName, Uri? proxyUri) { _poolManager = poolManager; _kind = kind; _proxyUri = proxyUri; - _maxConnections = maxConnections; + _maxConnections = Settings._maxConnectionsPerServer; if (host != null) { @@ -174,6 +173,11 @@ public HttpConnectionPool(HttpConnectionPoolManager poolManager, HttpConnectionK Debug.Assert(sslHostName == null); Debug.Assert(proxyUri != null); + // Don't enforce the max connections limit on proxy tunnels; this would mean that connections to different origin servers + // would compete for the same limited number of connections. + // We will still enforce this limit on the user of the tunnel (i.e. ProxyTunnel or SslProxyTunnel). + _maxConnections = int.MaxValue; + _http2Enabled = false; _http3Enabled = false; break; @@ -290,7 +294,6 @@ private static SslClientAuthenticationOptions ConstructSslOptions(HttpConnection public HttpConnectionSettings Settings => _poolManager.Settings; public HttpConnectionKind Kind => _kind; public bool IsSecure => _kind == HttpConnectionKind.Https || _kind == HttpConnectionKind.SslProxyTunnel; - public bool AnyProxyKind => (_proxyUri != null); public Uri? ProxyUri => _proxyUri; public ICredentials? ProxyCredentials => _poolManager.ProxyCredentials; public byte[]? HostHeaderValueBytes => _hostHeaderValueBytes; @@ -1149,9 +1152,11 @@ public async Task SendWithNtConnectionAuthAsync(HttpConnect } } + private bool DoProxyAuth => (_kind == HttpConnectionKind.Proxy || _kind == HttpConnectionKind.ProxyConnect); + public Task SendWithNtProxyAuthAsync(HttpConnection connection, HttpRequestMessage request, bool async, CancellationToken cancellationToken) { - if (AnyProxyKind && ProxyCredentials != null) + if (DoProxyAuth && ProxyCredentials is not null) { return AuthenticationHelper.SendWithNtProxyAuthAsync(request, ProxyUri!, async, ProxyCredentials, connection, this, cancellationToken); } @@ -1159,13 +1164,11 @@ public Task SendWithNtProxyAuthAsync(HttpConnection connect return connection.SendAsync(request, async, cancellationToken); } - public ValueTask SendWithProxyAuthAsync(HttpRequestMessage request, bool async, bool doRequestAuth, CancellationToken cancellationToken) { - if ((_kind == HttpConnectionKind.Proxy || _kind == HttpConnectionKind.ProxyConnect) && - _poolManager.ProxyCredentials != null) + if (DoProxyAuth && ProxyCredentials is not null) { - return AuthenticationHelper.SendWithProxyAuthAsync(request, _proxyUri!, async, _poolManager.ProxyCredentials, doRequestAuth, this, cancellationToken); + return AuthenticationHelper.SendWithProxyAuthAsync(request, _proxyUri!, async, ProxyCredentials, doRequestAuth, this, cancellationToken); } return SendWithRetryAsync(request, async, doRequestAuth, cancellationToken); diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPoolManager.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPoolManager.cs index c7f70725895ecc..b8fafcca136142 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPoolManager.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPoolManager.cs @@ -39,9 +39,7 @@ internal sealed class HttpConnectionPoolManager : IDisposable private readonly Timer? _cleaningTimer; /// Heart beat timer currently used for Http2 ping only. private readonly Timer? _heartBeatTimer; - /// The maximum number of connections allowed per pool. indicates unlimited. - private readonly int _maxConnectionsPerServer; - // Temporary + private readonly HttpConnectionSettings _settings; private readonly IWebProxy? _proxy; private readonly ICredentials? _proxyCredentials; @@ -60,7 +58,6 @@ internal sealed class HttpConnectionPoolManager : IDisposable public HttpConnectionPoolManager(HttpConnectionSettings settings) { _settings = settings; - _maxConnectionsPerServer = settings._maxConnectionsPerServer; _pools = new ConcurrentDictionary(); // As an optimization, we can sometimes avoid the overheads associated with @@ -321,7 +318,7 @@ public ValueTask SendAsyncCore(HttpRequestMessage request, HttpConnectionPool? pool; while (!_pools.TryGetValue(key, out pool)) { - pool = new HttpConnectionPool(this, key.Kind, key.Host, key.Port, key.SslHostName, key.ProxyUri, _maxConnectionsPerServer); + pool = new HttpConnectionPool(this, key.Kind, key.Host, key.Port, key.SslHostName, key.ProxyUri); if (_cleaningTimer == null) { diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs index 2176fceaf75322..2ff459dfd710aa 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs @@ -682,7 +682,7 @@ public void Dispose_UseAfterDispose_Throws() [Theory] [InlineData(false)] [InlineData(true)] - [SkipOnMono("System.Net.Sockets is not supported on this platform", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "System.Net.Sockets is not supported on this platform")] public void CancelAllPending_AllPendingOperationsCanceled(bool withInfiniteTimeout) { using (var client = new HttpClient(new CustomResponseHandler((r, c) => WhenCanceled(c)))) @@ -700,7 +700,7 @@ public void CancelAllPending_AllPendingOperationsCanceled(bool withInfiniteTimeo [Theory] [InlineData(HttpCompletionOption.ResponseContentRead)] [InlineData(HttpCompletionOption.ResponseHeadersRead)] - [SkipOnMono("System.Net.Sockets is not supported on this platform", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "System.Net.Sockets is not supported on this platform")] public void Timeout_TooShort_AllPendingOperationsCanceled(HttpCompletionOption completionOption) { using (var client = new HttpClient(new CustomResponseHandler((r, c) => WhenCanceled(c)))) @@ -736,7 +736,7 @@ public async Task Timeout_CallerCanceledTokenAfterTimeout_TimeoutIsNotDetected(H [Theory] [InlineData(HttpCompletionOption.ResponseContentRead)] [InlineData(HttpCompletionOption.ResponseHeadersRead)] - [SkipOnMono("System.Net.Sockets is not supported on this platform", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "System.Net.Sockets is not supported on this platform")] public void Timeout_CallerCanceledTokenBeforeTimeout_TimeoutIsNotDetected(HttpCompletionOption completionOption) { using (var client = new HttpClient(new CustomResponseHandler((r, c) => WhenCanceled(c)))) diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpContentTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpContentTest.cs index 2ffffbdc966c0f..eae8a3a6b3a4e9 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpContentTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpContentTest.cs @@ -298,7 +298,7 @@ public async Task ReadAsStreamAsync_FirstGetFromUnbufferedContentThenGetFromBuff Assert.Equal(before, after); } - [SkipOnMono("Browser doesn't support Synchronous reads", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Browser doesn't support Synchronous reads")] [Theory] [InlineData(true)] [InlineData(false)] diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs index 7bf08ccfddf210..aa0efb21fd923d 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs @@ -981,7 +981,7 @@ public sealed class SocketsHttpHandler_SchSendAuxRecordHttpTest : SchSendAuxReco public SocketsHttpHandler_SchSendAuxRecordHttpTest(ITestOutputHelper output) : base(output) { } } - [SkipOnMono("Tests hang with chrome. To be investigated", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Tests hang with chrome. To be investigated")] public sealed class SocketsHttpHandler_HttpClientHandlerTest : HttpClientHandlerTest { public SocketsHttpHandler_HttpClientHandlerTest(ITestOutputHelper output) : base(output) { } diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SyncHttpHandlerTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SyncHttpHandlerTest.cs index 98a3493264e592..22db83c64900c6 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SyncHttpHandlerTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SyncHttpHandlerTest.cs @@ -102,7 +102,7 @@ public SyncHttpHandler_Connect_Test(ITestOutputHelper output) : base(output) { } protected override bool TestAsync => false; } - [SkipOnMono("System.Net.Sockets is not supported on this platform.", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "System.Net.Sockets is not supported on this platform.")] public sealed class SyncHttpHandlerTest_HttpClientHandlerTest_Headers : HttpClientHandlerTest_Headers { public SyncHttpHandlerTest_HttpClientHandlerTest_Headers(ITestOutputHelper output) : base(output) { } diff --git a/src/libraries/System.Net.Http/tests/UnitTests/DigestAuthenticationTests.cs b/src/libraries/System.Net.Http/tests/UnitTests/DigestAuthenticationTests.cs index 036aa2bba8aaf3..cc2fa1fca455c5 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/DigestAuthenticationTests.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/DigestAuthenticationTests.cs @@ -8,7 +8,7 @@ namespace System.Net.Http.Tests { - [SkipOnMono("System.Security.Cryptography is not supported on Browser, see https://github.com/dotnet/runtime/pull/38379", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "System.Security.Cryptography is not supported on Browser, see https://github.com/dotnet/runtime/pull/38379")] public class DigestAuthenticationTests { private static readonly List s_keyListWithCountTwo = new List { "key1", "key2" }; diff --git a/src/libraries/System.Net.HttpListener/tests/AssemblyInfo.cs b/src/libraries/System.Net.HttpListener/tests/AssemblyInfo.cs index be691cadde455f..2c4b07bdf7aa87 100644 --- a/src/libraries/System.Net.HttpListener/tests/AssemblyInfo.cs +++ b/src/libraries/System.Net.HttpListener/tests/AssemblyInfo.cs @@ -3,4 +3,4 @@ using Xunit; -[assembly: SkipOnMono("System.Net.HttpListener is not supported on wasm", TestPlatforms.Browser)] \ No newline at end of file +[assembly: SkipOnPlatform(TestPlatforms.Browser, "System.Net.HttpListener is not supported on Browser")] \ No newline at end of file diff --git a/src/libraries/System.Net.Mail/tests/Functional/LoggingTest.cs b/src/libraries/System.Net.Mail/tests/Functional/LoggingTest.cs index 34f29d5b65032e..c4c9e801fd78c6 100644 --- a/src/libraries/System.Net.Mail/tests/Functional/LoggingTest.cs +++ b/src/libraries/System.Net.Mail/tests/Functional/LoggingTest.cs @@ -9,7 +9,7 @@ namespace System.Net.Mail.Tests { - [PlatformSpecific(~TestPlatforms.Browser)] // SmtpClient is not supported on Browser + [SkipOnPlatform(TestPlatforms.Browser, "SmtpClient is not supported on Browser")] public class LoggingTest { [Fact] diff --git a/src/libraries/System.Net.Mail/tests/Functional/MailMessageTest.cs b/src/libraries/System.Net.Mail/tests/Functional/MailMessageTest.cs index e891a35d8b35d8..99a0e7727c7c2d 100644 --- a/src/libraries/System.Net.Mail/tests/Functional/MailMessageTest.cs +++ b/src/libraries/System.Net.Mail/tests/Functional/MailMessageTest.cs @@ -149,7 +149,7 @@ public void SubjectAndEncodingTest() } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] // Not passing as internal System.Net.Mail.MailWriter stripped from build + [SkipOnPlatform(TestPlatforms.Browser, "Not passing as internal System.Net.Mail.MailWriter stripped from build")] public void SendMailMessageTest() { string expected = @"X-Sender: from@example.com @@ -202,7 +202,7 @@ blah blah } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] // Not passing as internal System.Net.Mail.MailWriter stripped from build + [SkipOnPlatform(TestPlatforms.Browser, "Not passing as internal System.Net.Mail.MailWriter stripped from build")] public void SentSpecialLengthMailAttachment_Base64Decode_Success() { // The special length follows pattern: (3N - 1) * 0x4400 + 1 diff --git a/src/libraries/System.Net.Mail/tests/Functional/SmtpClientCredentialsTest.cs b/src/libraries/System.Net.Mail/tests/Functional/SmtpClientCredentialsTest.cs index 64d884412b8fdf..b7618813ce902f 100644 --- a/src/libraries/System.Net.Mail/tests/Functional/SmtpClientCredentialsTest.cs +++ b/src/libraries/System.Net.Mail/tests/Functional/SmtpClientCredentialsTest.cs @@ -11,7 +11,7 @@ namespace System.Net.Mail.Functional.Tests { - [PlatformSpecific(~TestPlatforms.Browser)] // SmtpClient is not supported on Browser + [SkipOnPlatform(TestPlatforms.Browser, "SmtpClient is not supported on Browser")] public class SmtpClientCredentialsTest { private readonly string UserName = "user"; diff --git a/src/libraries/System.Net.Mail/tests/Functional/SmtpClientTest.cs b/src/libraries/System.Net.Mail/tests/Functional/SmtpClientTest.cs index 45ab7b583e3879..bd0fb5298432d1 100644 --- a/src/libraries/System.Net.Mail/tests/Functional/SmtpClientTest.cs +++ b/src/libraries/System.Net.Mail/tests/Functional/SmtpClientTest.cs @@ -19,7 +19,7 @@ namespace System.Net.Mail.Tests { - [PlatformSpecific(~TestPlatforms.Browser)] // SmtpClient is not supported on Browser + [SkipOnPlatform(TestPlatforms.Browser, "SmtpClient is not supported on Browser")] public class SmtpClientTest : FileCleanupTestBase { private SmtpClient _smtp; @@ -312,7 +312,7 @@ public void TestMailDelivery() [Fact] // [ActiveIssue("https://github.com/dotnet/runtime/issues/31719")] [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, ".NET Framework has a bug and may not time out for low values")] - [PlatformSpecific(~TestPlatforms.OSX)] // on OSX, not all synchronous operations (e.g. connect) can be aborted by closing the socket. + [SkipOnPlatform(TestPlatforms.OSX, "on OSX, not all synchronous operations (e.g. connect) can be aborted by closing the socket.")] public void TestZeroTimeout() { var testTask = Task.Run(() => diff --git a/src/libraries/System.Net.Mail/tests/Unit/SmtpConnectionTests/EhloParseExtensionsTest.cs b/src/libraries/System.Net.Mail/tests/Unit/SmtpConnectionTests/EhloParseExtensionsTest.cs index f0c3f437c180da..2dae9457bd8612 100644 --- a/src/libraries/System.Net.Mail/tests/Unit/SmtpConnectionTests/EhloParseExtensionsTest.cs +++ b/src/libraries/System.Net.Mail/tests/Unit/SmtpConnectionTests/EhloParseExtensionsTest.cs @@ -5,7 +5,7 @@ namespace System.Net.Mail.Tests { - [PlatformSpecific(~TestPlatforms.Browser)] // SmtpClient is not supported on Browser + [SkipOnPlatform(TestPlatforms.Browser, "SmtpClient is not supported on Browser")] public class EhloParseExtensionsTest { private SmtpConnection _smtpConnection; diff --git a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/AssemblyInfo.cs b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/AssemblyInfo.cs index cb9635c54427a4..c057237191b8a9 100644 --- a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/AssemblyInfo.cs +++ b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/AssemblyInfo.cs @@ -3,4 +3,4 @@ using Xunit; -[assembly: SkipOnMono("System.Net.NameResolution is not supported on wasm.", TestPlatforms.Browser)] +[assembly: SkipOnPlatform(TestPlatforms.Browser, "System.Net.NameResolution is not supported on Browser.")] diff --git a/src/libraries/System.Net.NameResolution/tests/PalTests/AssemblyInfo.cs b/src/libraries/System.Net.NameResolution/tests/PalTests/AssemblyInfo.cs index cb9635c54427a4..c057237191b8a9 100644 --- a/src/libraries/System.Net.NameResolution/tests/PalTests/AssemblyInfo.cs +++ b/src/libraries/System.Net.NameResolution/tests/PalTests/AssemblyInfo.cs @@ -3,4 +3,4 @@ using Xunit; -[assembly: SkipOnMono("System.Net.NameResolution is not supported on wasm.", TestPlatforms.Browser)] +[assembly: SkipOnPlatform(TestPlatforms.Browser, "System.Net.NameResolution is not supported on Browser.")] diff --git a/src/libraries/System.Net.NameResolution/tests/UnitTests/AssemblyInfo.cs b/src/libraries/System.Net.NameResolution/tests/UnitTests/AssemblyInfo.cs index cb9635c54427a4..c057237191b8a9 100644 --- a/src/libraries/System.Net.NameResolution/tests/UnitTests/AssemblyInfo.cs +++ b/src/libraries/System.Net.NameResolution/tests/UnitTests/AssemblyInfo.cs @@ -3,4 +3,4 @@ using Xunit; -[assembly: SkipOnMono("System.Net.NameResolution is not supported on wasm.", TestPlatforms.Browser)] +[assembly: SkipOnPlatform(TestPlatforms.Browser, "System.Net.NameResolution is not supported on Browser.")] diff --git a/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/AssemblyInfo.cs b/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/AssemblyInfo.cs index 82a863731002f6..94698a9536f2f9 100644 --- a/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/AssemblyInfo.cs +++ b/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/AssemblyInfo.cs @@ -3,4 +3,4 @@ using Xunit; -[assembly: SkipOnMono("System.Net.NetworkInformation is not supported on wasm", TestPlatforms.Browser)] +[assembly: SkipOnPlatform(TestPlatforms.Browser, "System.Net.NetworkInformation is not supported on Browser")] diff --git a/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/NetworkInterfaceBasicTest.cs b/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/NetworkInterfaceBasicTest.cs index f4452e06d7c792..705d8a9aed1496 100644 --- a/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/NetworkInterfaceBasicTest.cs +++ b/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/NetworkInterfaceBasicTest.cs @@ -274,7 +274,7 @@ public void BasicTest_GetIsNetworkAvailable_Success() [Theory] [ActiveIssue("https://github.com/dotnet/runtime/issues/34690", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] - [PlatformSpecific(~(TestPlatforms.OSX|TestPlatforms.FreeBSD))] + [SkipOnPlatform(TestPlatforms.OSX | TestPlatforms.FreeBSD, "Expected behavior is different on OSX or FreeBSD")] [InlineData(false)] [InlineData(true)] public async Task NetworkInterface_LoopbackInterfaceIndex_MatchesReceivedPackets(bool ipv6) diff --git a/src/libraries/System.Net.Ping/src/System.Net.Ping.csproj b/src/libraries/System.Net.Ping/src/System.Net.Ping.csproj index e2913296b35495..232603843d04e7 100644 --- a/src/libraries/System.Net.Ping/src/System.Net.Ping.csproj +++ b/src/libraries/System.Net.Ping/src/System.Net.Ping.csproj @@ -102,6 +102,7 @@ + diff --git a/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.Unix.cs b/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.Unix.cs index 74abb2a4adf256..0942f4906ec347 100644 --- a/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.Unix.cs +++ b/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.Unix.cs @@ -295,6 +295,8 @@ private Process GetPingProcess(IPAddress address, byte[] buffer, int timeout, Pi ProcessStartInfo psi = new ProcessStartInfo(pingExecutable, processArgs); psi.RedirectStandardOutput = true; psi.RedirectStandardError = true; + // Set LC_ALL=C to make sure to get ping output which is not affected by locale environment variables such as LANG and LC_MESSAGES. + psi.EnvironmentVariables["LC_ALL"] = "C"; return new Process() { StartInfo = psi }; } diff --git a/src/libraries/System.Net.Ping/tests/FunctionalTests/AssemblyInfo.cs b/src/libraries/System.Net.Ping/tests/FunctionalTests/AssemblyInfo.cs index 49779079e46c81..da717221f60416 100644 --- a/src/libraries/System.Net.Ping/tests/FunctionalTests/AssemblyInfo.cs +++ b/src/libraries/System.Net.Ping/tests/FunctionalTests/AssemblyInfo.cs @@ -3,4 +3,4 @@ using Xunit; -[assembly: SkipOnMono("System.Net.Ping is not supported on Browser", TestPlatforms.Browser)] +[assembly: SkipOnPlatform(TestPlatforms.Browser, "System.Net.Ping is not supported on Browser")] diff --git a/src/libraries/System.Net.Ping/tests/FunctionalTests/PingTest.cs b/src/libraries/System.Net.Ping/tests/FunctionalTests/PingTest.cs index 096103eb66601d..e01e940eb96c42 100644 --- a/src/libraries/System.Net.Ping/tests/FunctionalTests/PingTest.cs +++ b/src/libraries/System.Net.Ping/tests/FunctionalTests/PingTest.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.DotNet.XUnitExtensions; +using System.Diagnostics; using System.Linq; using System.Net.Sockets; using System.Net.Test.Common; @@ -915,5 +916,73 @@ public void SendPingWithIPAddressAndTimeoutAndBufferAndPingOptions_ElevatedUnix( }); }, localIpAddress.ToString(), new RemoteInvokeOptions { RunAsSudo = true }).Dispose(); } + + [PlatformSpecific(TestPlatforms.AnyUnix)] + [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [InlineData(AddressFamily.InterNetwork, "ja_JP.UTF8", null, null)] + [InlineData(AddressFamily.InterNetwork, "en_US.UTF8", "ja_JP.UTF8", null)] + [InlineData(AddressFamily.InterNetwork, "en_US.UTF8", null, "ja_JP.UTF8")] + [InlineData(AddressFamily.InterNetworkV6, "ja_JP.UTF8", null, null)] + [InlineData(AddressFamily.InterNetworkV6, "en_US.UTF8", "ja_JP.UTF8", null)] + [InlineData(AddressFamily.InterNetworkV6, "en_US.UTF8", null, "ja_JP.UTF8")] + public void SendPing_LocaleEnvVarsMustBeIgnored(AddressFamily addressFamily, string envVar_LANG, string envVar_LC_MESSAGES, string envVar_LC_ALL) + { + IPAddress localIpAddress = TestSettings.GetLocalIPAddress(addressFamily); + if (localIpAddress == null) + { + // No local address for given address family. + return; + } + + var remoteInvokeStartInfo = new ProcessStartInfo(); + + remoteInvokeStartInfo.EnvironmentVariables["LANG"] = envVar_LANG; + remoteInvokeStartInfo.EnvironmentVariables["LC_MESSAGES"] = envVar_LC_MESSAGES; + remoteInvokeStartInfo.EnvironmentVariables["LC_ALL"] = envVar_LC_ALL; + + RemoteExecutor.Invoke(address => + { + SendBatchPing( + (ping) => ping.Send(address, TestSettings.PingTimeout), + (pingReply) => + { + PingResultValidator(pingReply, new IPAddress[] { IPAddress.Parse(address) }, null); + }); + }, localIpAddress.ToString(), new RemoteInvokeOptions { StartInfo = remoteInvokeStartInfo }).Dispose(); + } + + [PlatformSpecific(TestPlatforms.AnyUnix)] + [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [InlineData(AddressFamily.InterNetwork, "ja_JP.UTF8", null, null)] + [InlineData(AddressFamily.InterNetwork, "en_US.UTF8", "ja_JP.UTF8", null)] + [InlineData(AddressFamily.InterNetwork, "en_US.UTF8", null, "ja_JP.UTF8")] + [InlineData(AddressFamily.InterNetworkV6, "ja_JP.UTF8", null, null)] + [InlineData(AddressFamily.InterNetworkV6, "en_US.UTF8", "ja_JP.UTF8", null)] + [InlineData(AddressFamily.InterNetworkV6, "en_US.UTF8", null, "ja_JP.UTF8")] + public void SendPingAsync_LocaleEnvVarsMustBeIgnored(AddressFamily addressFamily, string envVar_LANG, string envVar_LC_MESSAGES, string envVar_LC_ALL) + { + IPAddress localIpAddress = TestSettings.GetLocalIPAddress(addressFamily); + if (localIpAddress == null) + { + // No local address for given address family. + return; + } + + var remoteInvokeStartInfo = new ProcessStartInfo(); + + remoteInvokeStartInfo.EnvironmentVariables["LANG"] = envVar_LANG; + remoteInvokeStartInfo.EnvironmentVariables["LC_MESSAGES"] = envVar_LC_MESSAGES; + remoteInvokeStartInfo.EnvironmentVariables["LC_ALL"] = envVar_LC_ALL; + + RemoteExecutor.Invoke(async address => + { + await SendBatchPingAsync( + (ping) => ping.SendPingAsync(address), + (pingReply) => + { + PingResultValidator(pingReply, new IPAddress[] { IPAddress.Parse(address) }, null); + }); + }, localIpAddress.ToString(), new RemoteInvokeOptions { StartInfo = remoteInvokeStartInfo }).Dispose(); + } } } diff --git a/src/libraries/System.Net.Primitives/tests/FunctionalTests/SocketAddressTest.cs b/src/libraries/System.Net.Primitives/tests/FunctionalTests/SocketAddressTest.cs index af2a89c4807a50..5dca4b2b62c538 100644 --- a/src/libraries/System.Net.Primitives/tests/FunctionalTests/SocketAddressTest.cs +++ b/src/libraries/System.Net.Primitives/tests/FunctionalTests/SocketAddressTest.cs @@ -98,7 +98,7 @@ public static void ToString_LegacyUnknownFamily_Success(AddressFamily family) [Theory] [InlineData(AddressFamily.Packet)] [InlineData(AddressFamily.ControllerAreaNetwork)] - [PlatformSpecific(~(TestPlatforms.Linux | TestPlatforms.Browser))] + [SkipOnPlatform(TestPlatforms.Linux | TestPlatforms.Browser, "Expected behavior is different on Linux or Browser")] public static void ToString_UnsupportedFamily_Throws(AddressFamily family) { Assert.Throws(() => new SocketAddress(family)); diff --git a/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj b/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj index 6ad41a39fdf03f..8ef23c8a93b4f9 100644 --- a/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj +++ b/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj @@ -52,6 +52,7 @@ + @@ -66,6 +67,10 @@ PreserveNewest PreserveNewest + + PreserveNewest + PreserveNewest + PreserveNewest PreserveNewest diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicEnums.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicEnums.cs index 34dd31bf70ce83..33ffea25d1cecb 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicEnums.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicEnums.cs @@ -19,7 +19,7 @@ internal enum QUIC_CREDENTIAL_TYPE : uint CONTEXT, FILE, FILE_PROTECTED, - STUB_NULL = 0xF0000000, // Pass as server cert to stubtls implementation. + PKCS12, } [Flags] 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 131227746c3ad3..f3ea17bd0360c4 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 @@ -215,30 +215,33 @@ internal struct CredentialConfig internal struct CredentialConfigCertificateUnion { [FieldOffset(0)] - internal CredentialConfigCertificateCertificateHash CertificateHash; + internal CredentialConfigCertificateHash CertificateHash; [FieldOffset(0)] - internal CredentialConfigCertificateCertificateHashStore CertificateHashStore; + internal CredentialConfigCertificateHashStore CertificateHashStore; [FieldOffset(0)] internal IntPtr CertificateContext; [FieldOffset(0)] - internal CredentialConfigCertificateCertificateFile CertificateFile; + internal CredentialConfigCertificateFile CertificateFile; [FieldOffset(0)] - internal CredentialConfigCertificateCertificateFileProtected CertificateFileProtected; + internal CredentialConfigCertificateFileProtected CertificateFileProtected; + + [FieldOffset(0)] + internal CredentialConfigCertificatePkcs12 CertificatePkcs12; } [StructLayout(LayoutKind.Sequential)] - internal struct CredentialConfigCertificateCertificateHash + internal struct CredentialConfigCertificateHash { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] internal byte[] ShaHash; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - internal struct CredentialConfigCertificateCertificateHashStore + internal struct CredentialConfigCertificateHashStore { internal QUIC_CERTIFICATE_HASH_STORE_FLAGS Flags; @@ -250,7 +253,7 @@ internal struct CredentialConfigCertificateCertificateHashStore } [StructLayout(LayoutKind.Sequential)] - internal struct CredentialConfigCertificateCertificateFile + internal struct CredentialConfigCertificateFile { [MarshalAs(UnmanagedType.LPUTF8Str)] internal string PrivateKeyFile; @@ -260,7 +263,7 @@ internal struct CredentialConfigCertificateCertificateFile } [StructLayout(LayoutKind.Sequential)] - internal struct CredentialConfigCertificateCertificateFileProtected + internal struct CredentialConfigCertificateFileProtected { [MarshalAs(UnmanagedType.LPUTF8Str)] internal string PrivateKeyFile; @@ -272,6 +275,16 @@ internal struct CredentialConfigCertificateCertificateFileProtected internal string PrivateKeyPassword; } + [StructLayout(LayoutKind.Sequential)] + internal struct CredentialConfigCertificatePkcs12 + { + internal IntPtr Asn1Blob; + + internal uint Asn1BlobLength; + + internal IntPtr PrivateKeyPassword; + } + [StructLayout(LayoutKind.Sequential)] internal struct ListenerEvent { @@ -407,6 +420,14 @@ internal struct ConnectionEventDataStreamsAvailable internal ushort UniDirectionalCount; } + [StructLayout(LayoutKind.Sequential)] + internal struct ConnectionEventPeerCertificateReceived + { + internal IntPtr PlatformCertificateHandle; + internal uint DeferredErrorFlags; + internal uint DeferredStatus; + } + [StructLayout(LayoutKind.Explicit)] internal struct ConnectionEventDataUnion { @@ -434,7 +455,10 @@ internal struct ConnectionEventDataUnion [FieldOffset(0)] internal ConnectionEventDataStreamsAvailable StreamsAvailable; - // TODO: missing IDEAL_PROCESSOR_CHANGED, ..., PEER_CERTIFICATE_RECEIVED (7 total) + [FieldOffset(0)] + internal ConnectionEventPeerCertificateReceived PeerCertificateReceived; + + // TODO: missing IDEAL_PROCESSOR_CHANGED, ..., (6 total) } [StructLayout(LayoutKind.Sequential)] diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicStatusCodes.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicStatusCodes.cs index c48fac85f98e6d..50f736d429f7f6 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicStatusCodes.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicStatusCodes.cs @@ -5,12 +5,14 @@ namespace System.Net.Quic.Implementations.MsQuic.Internal { internal static class MsQuicStatusCodes { - internal static uint Success => OperatingSystem.IsWindows() ? Windows.Success : Linux.Success; - internal static uint Pending => OperatingSystem.IsWindows() ? Windows.Pending : Linux.Pending; - internal static uint InternalError => OperatingSystem.IsWindows() ? Windows.InternalError : Linux.InternalError; + internal static uint Success => OperatingSystem.IsWindows() ? Windows.Success : Posix.Success; + internal static uint Pending => OperatingSystem.IsWindows() ? Windows.Pending : Posix.Pending; + internal static uint InternalError => OperatingSystem.IsWindows() ? Windows.InternalError : Posix.InternalError; + internal static uint InvalidState => OperatingSystem.IsWindows() ? Windows.InvalidState : Posix.InvalidState; + internal static uint HandshakeFailure => OperatingSystem.IsWindows() ? Windows.HandshakeFailure : Posix.HandshakeFailure; // TODO return better error messages here. - public static string GetError(uint status) => OperatingSystem.IsWindows() ? Windows.GetError(status) : Linux.GetError(status); + public static string GetError(uint status) => OperatingSystem.IsWindows() ? Windows.GetError(status) : Posix.GetError(status); private static class Windows { @@ -69,7 +71,7 @@ public static string GetError(uint status) } } - private static class Linux + private static class Posix { internal const uint Success = 0; internal const uint Pending = unchecked((uint)-2); diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicStatusHelper.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicStatusHelper.cs index 2f64fa241582d3..4f2bedb1410364 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicStatusHelper.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicStatusHelper.cs @@ -12,7 +12,7 @@ internal static bool SuccessfulStatusCode(uint status) return status < 0x80000000; } - if (OperatingSystem.IsLinux()) + if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) { return (int)status <= 0; } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs index 24a5b2fa5f9210..cc3d21140ec473 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs @@ -7,6 +7,7 @@ using System.Net.Security; using System.Runtime.InteropServices; using System.Security.Cryptography.X509Certificates; +using System.Text; using System.Threading; using static System.Net.Quic.Implementations.MsQuic.Internal.MsQuicNativeMethods; @@ -59,6 +60,18 @@ private static unsafe SafeMsQuicConfigurationHandle Create(QuicOptions options, throw new Exception("MaxBidirectionalStreams overflow."); } + if ((flags & QUIC_CREDENTIAL_FLAGS.CLIENT) == 0) + { + if (certificate == null) + { + throw new Exception("Server must provide certificate"); + } + } + else + { + flags |= QUIC_CREDENTIAL_FLAGS.INDICATE_CERTIFICATE_RECEIVED | QUIC_CREDENTIAL_FLAGS.NO_CERTIFICATE_VALIDATION; + } + Debug.Assert(!MsQuicApi.Api.Registration.IsInvalid); var settings = new QuicSettings @@ -99,31 +112,39 @@ private static unsafe SafeMsQuicConfigurationHandle Create(QuicOptions options, try { - // TODO: find out what to do for OpenSSL here -- passing handle won't work, because - // MsQuic has a private copy of OpenSSL so the SSL_CTX will be incompatible. - CredentialConfig config = default; - config.Flags = flags; // TODO: consider using LOAD_ASYNCHRONOUS with a callback. if (certificate != null) { -#if true - // If using stub TLS. - config.Type = QUIC_CREDENTIAL_TYPE.STUB_NULL; -#else - // TODO: doesn't work on non-Windows - config.Type = QUIC_CREDENTIAL_TYPE.CONTEXT; - config.Certificate = certificate.Handle; -#endif + if (OperatingSystem.IsWindows()) + { + config.Type = QUIC_CREDENTIAL_TYPE.CONTEXT; + config.Certificate = certificate.Handle; + status = MsQuicApi.Api.ConfigurationLoadCredentialDelegate(configurationHandle, ref config); + } + else + { + CredentialConfigCertificatePkcs12 pkcs12Config; + byte[] asn1 = certificate.Export(X509ContentType.Pkcs12); + fixed (void* ptr = asn1) + { + pkcs12Config.Asn1Blob = (IntPtr)ptr; + pkcs12Config.Asn1BlobLength = (uint)asn1.Length; + pkcs12Config.PrivateKeyPassword = IntPtr.Zero; + + config.Type = QUIC_CREDENTIAL_TYPE.PKCS12; + config.Certificate = (IntPtr)(&pkcs12Config); + status = MsQuicApi.Api.ConfigurationLoadCredentialDelegate(configurationHandle, ref config); + } + } } else { - // TODO: not allowed for OpenSSL and server config.Type = QUIC_CREDENTIAL_TYPE.NONE; + status = MsQuicApi.Api.ConfigurationLoadCredentialDelegate(configurationHandle, ref config); } - status = MsQuicApi.Api.ConfigurationLoadCredentialDelegate(configurationHandle, ref config); QuicExceptionHelpers.ThrowIfFailed(status, "ConfigurationLoadCredential failed."); } catch diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs index 87897c19511594..24392320c923c8 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs @@ -7,6 +7,8 @@ using System.Net.Sockets; using System.Runtime.ExceptionServices; using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Threading.Channels; using System.Threading.Tasks; @@ -16,6 +18,9 @@ namespace System.Net.Quic.Implementations.MsQuic { internal sealed class MsQuicConnection : QuicConnectionProvider { + private static readonly Oid s_clientAuthOid = new Oid("1.3.6.1.5.5.7.3.2", "1.3.6.1.5.5.7.3.2"); + private static readonly Oid s_serverAuthOid = new Oid("1.3.6.1.5.5.7.3.1", "1.3.6.1.5.5.7.3.1"); + // Delegate that wraps the static function that will be called when receiving an event. private static readonly ConnectionCallbackDelegate s_connectionDelegate = new ConnectionCallbackDelegate(NativeCallbackHandler); @@ -30,6 +35,10 @@ internal sealed class MsQuicConnection : QuicConnectionProvider private IPEndPoint? _localEndPoint; private readonly EndPoint _remoteEndPoint; private SslApplicationProtocol _negotiatedAlpnProtocol; + private bool _isServer; + private bool _remoteCertificateRequired; + private X509RevocationMode _revocationMode = X509RevocationMode.Offline; + private RemoteCertificateValidationCallback? _remoteCertificateValidationCallback; private sealed class State { @@ -61,6 +70,8 @@ public MsQuicConnection(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, Saf _state.Connected = true; _localEndPoint = localEndPoint; _remoteEndPoint = remoteEndPoint; + _remoteCertificateRequired = false; + _isServer = true; _stateHandle = GCHandle.Alloc(_state); @@ -83,6 +94,13 @@ public MsQuicConnection(QuicClientConnectionOptions options) { _remoteEndPoint = options.RemoteEndPoint!; _configuration = SafeMsQuicConfigurationHandle.Create(options); + _isServer = false; + _remoteCertificateRequired = true; + if (options.ClientAuthenticationOptions != null) + { + _revocationMode = options.ClientAuthenticationOptions.CertificateRevocationCheckMode; + _remoteCertificateValidationCallback = options.ClientAuthenticationOptions.RemoteCertificateValidationCallback; + } _stateHandle = GCHandle.Alloc(_state); try @@ -181,6 +199,75 @@ private static uint HandleEventStreamsAvailable(State state, ref ConnectionEvent return MsQuicStatusCodes.Success; } + private static uint HandleEventPeerCertificateReceived(State state, ref ConnectionEvent connectionEvent) + { + SslPolicyErrors sslPolicyErrors = SslPolicyErrors.None; + X509Chain? chain = null; + X509Certificate2? certificate = null; + + if (!OperatingSystem.IsWindows()) + { + // TODO fix validation with OpenSSL + return MsQuicStatusCodes.Success; + } + + MsQuicConnection? connection = state.Connection; + if (connection == null) + { + return MsQuicStatusCodes.InvalidState; + } + + if (connectionEvent.Data.PeerCertificateReceived.PlatformCertificateHandle != IntPtr.Zero) + { + certificate = new X509Certificate2(connectionEvent.Data.PeerCertificateReceived.PlatformCertificateHandle); + } + + try + { + if (certificate == null) + { + if (NetEventSource.Log.IsEnabled() && connection._remoteCertificateRequired) NetEventSource.Error(state.Connection, $"Remote certificate required, but no remote certificate received"); + sslPolicyErrors |= SslPolicyErrors.RemoteCertificateNotAvailable; + } + else + { + chain = new X509Chain(); + chain.ChainPolicy.RevocationMode = connection._revocationMode; + chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot; + chain.ChainPolicy.ApplicationPolicy.Add(connection._isServer ? s_clientAuthOid : s_serverAuthOid); + + if (!chain.Build(certificate)) + { + sslPolicyErrors |= SslPolicyErrors.RemoteCertificateChainErrors; + } + } + + if (!connection._remoteCertificateRequired) + { + sslPolicyErrors &= ~SslPolicyErrors.RemoteCertificateNotAvailable; + } + + if (connection._remoteCertificateValidationCallback != null) + { + bool success = connection._remoteCertificateValidationCallback(connection, certificate, chain, sslPolicyErrors); + if (!success && NetEventSource.Log.IsEnabled()) + NetEventSource.Error(state.Connection, "Remote certificate rejected by verification callback"); + return success ? MsQuicStatusCodes.Success : MsQuicStatusCodes.HandshakeFailure; + } + + if (NetEventSource.Log.IsEnabled()) + NetEventSource.Info(state.Connection, $"Certificate validation for '${certificate?.Subject}' finished with ${sslPolicyErrors}"); + + return (sslPolicyErrors == SslPolicyErrors.None) ? MsQuicStatusCodes.Success : MsQuicStatusCodes.HandshakeFailure; + } + catch (Exception ex) + { + if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(state.Connection, $"Certificate validation failed ${ex.Message}"); + } + + return MsQuicStatusCodes.InternalError; + } + internal override async ValueTask AcceptStreamAsync(CancellationToken cancellationToken = default) { ThrowIfDisposed(); @@ -312,10 +399,9 @@ private static uint NativeCallbackHandler( ref ConnectionEvent connectionEvent) { var state = (State)GCHandle.FromIntPtr(context).Target!; - try { - switch ((QUIC_CONNECTION_EVENT_TYPE)connectionEvent.Type) + switch (connectionEvent.Type) { case QUIC_CONNECTION_EVENT_TYPE.CONNECTED: return HandleEventConnected(state, ref connectionEvent); @@ -329,6 +415,8 @@ private static uint NativeCallbackHandler( return HandleEventNewStream(state, ref connectionEvent); case QUIC_CONNECTION_EVENT_TYPE.STREAMS_AVAILABLE: return HandleEventStreamsAvailable(state, ref connectionEvent); + case QUIC_CONNECTION_EVENT_TYPE.PEER_CERTIFICATE_RECEIVED: + return HandleEventPeerCertificateReceived(state, ref connectionEvent); default: return MsQuicStatusCodes.Success; } diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTestBase.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTestBase.cs index 2178ec12c46fd2..d4ab76c0dcfa76 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTestBase.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTestBase.cs @@ -24,7 +24,8 @@ public SslClientAuthenticationOptions GetSslClientAuthenticationOptions() { return new SslClientAuthenticationOptions() { - ApplicationProtocols = new List() { new SslApplicationProtocol("quictest") } + ApplicationProtocols = new List() { new SslApplicationProtocol("quictest") }, + RemoteCertificateValidationCallback = (sender, certificate, chain, errors) => { return true; } }; } diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs index 6fbcc41bdc6d5f..53ddb009fa7bc7 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs @@ -31,7 +31,8 @@ public SslClientAuthenticationOptions GetSslClientAuthenticationOptions() { return new SslClientAuthenticationOptions() { - ApplicationProtocols = new List() { ApplicationProtocol } + ApplicationProtocols = new List() { ApplicationProtocol }, + RemoteCertificateValidationCallback = (sender, certificate, chain, errors) => { return true; } }; } diff --git a/src/libraries/System.Net.Requests/tests/AssemblyInfo.cs b/src/libraries/System.Net.Requests/tests/AssemblyInfo.cs index 67d13a0af274a1..82a9ebd805e958 100644 --- a/src/libraries/System.Net.Requests/tests/AssemblyInfo.cs +++ b/src/libraries/System.Net.Requests/tests/AssemblyInfo.cs @@ -4,4 +4,4 @@ using Xunit; [assembly: ActiveIssue("https://github.com/dotnet/runtime/issues/34690", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] -[assembly: SkipOnMono("System.Net.Requests is not supported on Browser.", TestPlatforms.Browser)] +[assembly: SkipOnPlatform(TestPlatforms.Browser, "System.Net.Requests is not supported on Browser.")] diff --git a/src/libraries/System.Net.Security/src/Resources/Strings.resx b/src/libraries/System.Net.Security/src/Resources/Strings.resx index 74c7555633d2a2..cf50b284e6f4f9 100644 --- a/src/libraries/System.Net.Security/src/Resources/Strings.resx +++ b/src/libraries/System.Net.Security/src/Resources/Strings.resx @@ -422,6 +422,9 @@ The requested combination of SslProtocols ({0}) is not valid for this platform because it skips intermediate versions. + + The requested SslProtocols ({0}) are not supported on this platform. + The '{0}' encryption policy is not supported on this platform. diff --git a/src/libraries/System.Net.Security/src/System.Net.Security.csproj b/src/libraries/System.Net.Security/src/System.Net.Security.csproj index 6c0a5b530fdf59..69f7d83079a3dd 100644 --- a/src/libraries/System.Net.Security/src/System.Net.Security.csproj +++ b/src/libraries/System.Net.Security/src/System.Net.Security.csproj @@ -331,11 +331,18 @@ Link="Common\System\Net\Security\Unix\SafeFreeSslCredentials.cs" /> + + + + @@ -369,6 +376,7 @@ Link="Common\Microsoft\Win32\SafeHandles\SafeCreateHandle.OSX.cs" /> + diff --git a/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.Android.cs b/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.Android.cs index 0289095ab53666..7643482f9336f5 100644 --- a/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.Android.cs +++ b/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.Android.cs @@ -24,10 +24,17 @@ internal static SslPolicyErrors VerifyCertificateProperties( ? SslPolicyErrors.None : SslPolicyErrors.RemoteCertificateChainErrors; - if (!checkCertName) - return errors; + if (checkCertName) + { + System.Diagnostics.Debug.Assert(hostName != null); + SafeDeleteSslContext sslContext = (SafeDeleteSslContext)securityContext; + if (!Interop.AndroidCrypto.SSLStreamVerifyHostname(sslContext.SslContext, hostName!)) + { + errors |= SslPolicyErrors.RemoteCertificateNameMismatch; + } + } - throw new NotImplementedException(nameof(VerifyCertificateProperties)); + return errors; } // @@ -63,7 +70,39 @@ internal static SslPolicyErrors VerifyCertificateProperties( if (sslContext == null) return null; - throw new NotImplementedException(nameof(GetRemoteCertificate)); + X509Certificate2? cert = null; + if (remoteCertificateStore == null) + { + // Constructing a new X509Certificate2 adds a global reference to the pointer, so we dispose this handle + using (SafeX509Handle handle = Interop.AndroidCrypto.SSLStreamGetPeerCertificate(sslContext)) + { + if (!handle.IsInvalid) + { + cert = new X509Certificate2(handle.DangerousGetHandle()); + } + } + } + else + { + IntPtr[] ptrs = Interop.AndroidCrypto.SSLStreamGetPeerCertificates(sslContext); + if (ptrs.Length > 0) + { + // This is intentionally a different object from the cert added to the remote certificate store + // to match the behaviour on other platforms. + cert = new X509Certificate2(ptrs[0]); + foreach (IntPtr ptr in ptrs) + { + // Constructing a new X509Certificate2 adds a global reference to the pointer, so we dispose this handle + using (var handle = new SafeX509Handle(ptr)) + { + remoteCertificateStore.Add(new X509Certificate2(handle.DangerousGetHandle())); + } + } + + } + } + + return cert; } // diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/SafeDeleteSslContext.cs b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/SafeDeleteSslContext.cs index 016b33b4ee3dab..69830d9ba75d68 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/SafeDeleteSslContext.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/SafeDeleteSslContext.cs @@ -6,15 +6,30 @@ using System.Net.Http; using System.Net.Security; using System.Security.Authentication; +using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using Microsoft.Win32.SafeHandles; +using PAL_KeyAlgorithm = Interop.AndroidCrypto.PAL_KeyAlgorithm; +using PAL_SSLStreamStatus = Interop.AndroidCrypto.PAL_SSLStreamStatus; + namespace System.Net { internal sealed class SafeDeleteSslContext : SafeDeleteContext { private const int InitialBufferSize = 2048; + private static readonly SslProtocols[] s_orderedSslProtocols = new SslProtocols[] + { + SslProtocols.Tls, + SslProtocols.Tls11, + SslProtocols.Tls12, + SslProtocols.Tls13, + }; + private static readonly Lazy s_supportedSslProtocols = new Lazy(Interop.AndroidCrypto.SSLGetSupportedProtocols); + private readonly SafeSslHandle _sslContext; + private readonly Interop.AndroidCrypto.SSLReadCallback _readCallback; + private readonly Interop.AndroidCrypto.SSLWriteCallback _writeCallback; private ArrayBuffer _inputBuffer = new ArrayBuffer(InitialBufferSize); private ArrayBuffer _outputBuffer = new ArrayBuffer(InitialBufferSize); @@ -26,8 +41,23 @@ public SafeDeleteSslContext(SafeFreeSslCredentials credential, SslAuthentication { Debug.Assert((credential != null) && !credential.IsInvalid, "Invalid credential used in SafeDeleteSslContext"); - _sslContext = new SafeSslHandle(); - throw new NotImplementedException(nameof(SafeDeleteSslContext)); + try + { + unsafe + { + _readCallback = ReadFromConnection; + _writeCallback = WriteToConnection; + } + + _sslContext = CreateSslContext(credential); + InitializeSslContext(_sslContext, _readCallback, _writeCallback, credential, authOptions); + } + catch (Exception ex) + { + Debug.Write("Exception Caught. - " + ex); + Dispose(); + throw; + } } public override bool IsInvalid => _sslContext?.IsInvalid ?? true; @@ -48,7 +78,7 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); } - private unsafe void WriteToConnection(byte* data, int offset, int dataLength) + private unsafe void WriteToConnection(byte* data, int dataLength) { var inputBuffer = new ReadOnlySpan(data, dataLength); @@ -57,19 +87,25 @@ private unsafe void WriteToConnection(byte* data, int offset, int dataLength) _outputBuffer.Commit(dataLength); } - private unsafe int ReadFromConnection(byte* data, int offset, int dataLength) + private unsafe PAL_SSLStreamStatus ReadFromConnection(byte* data, int* dataLength) { - if (dataLength == 0) - return 0; + int toRead = *dataLength; + if (toRead == 0) + return PAL_SSLStreamStatus.OK; if (_inputBuffer.ActiveLength == 0) - return 0; + { + *dataLength = 0; + return PAL_SSLStreamStatus.NeedData; + } - int toRead = Math.Min(dataLength, _inputBuffer.ActiveLength); + toRead = Math.Min(toRead, _inputBuffer.ActiveLength); _inputBuffer.ActiveSpan.Slice(0, toRead).CopyTo(new Span(data, toRead)); _inputBuffer.Discard(toRead); - return toRead; + + *dataLength = toRead; + return PAL_SSLStreamStatus.OK; } internal void Write(ReadOnlySpan buf) @@ -108,5 +144,101 @@ internal int ReadPendingWrites(byte[] buf, int offset, int count) return limit; } + + private static SafeSslHandle CreateSslContext(SafeFreeSslCredentials credential) + { + if (credential.CertificateContext == null) + { + return Interop.AndroidCrypto.SSLStreamCreate(); + } + + SslStreamCertificateContext context = credential.CertificateContext; + X509Certificate2 cert = context.Certificate; + Debug.Assert(context.Certificate.HasPrivateKey); + + PAL_KeyAlgorithm algorithm; + byte[] keyBytes; + using (AsymmetricAlgorithm key = GetPrivateKeyAlgorithm(cert, out algorithm)) + { + keyBytes = key.ExportPkcs8PrivateKey(); + } + IntPtr[] ptrs = new IntPtr[context.IntermediateCertificates.Length + 1]; + ptrs[0] = cert.Handle; + for (int i = 0; i < context.IntermediateCertificates.Length; i++) + { + ptrs[i + 1] = context.IntermediateCertificates[i].Handle; + } + + return Interop.AndroidCrypto.SSLStreamCreateWithCertificates(keyBytes, algorithm, ptrs); + } + + private static AsymmetricAlgorithm GetPrivateKeyAlgorithm(X509Certificate2 cert, out PAL_KeyAlgorithm algorithm) + { + AsymmetricAlgorithm? key = cert.GetRSAPrivateKey(); + if (key != null) + { + algorithm = PAL_KeyAlgorithm.RSA; + return key; + } + key = cert.GetECDsaPrivateKey(); + if (key != null) + { + algorithm = PAL_KeyAlgorithm.EC; + return key; + } + key = cert.GetDSAPrivateKey(); + if (key != null) + { + algorithm = PAL_KeyAlgorithm.DSA; + return key; + } + throw new NotSupportedException(SR.net_ssl_io_no_server_cert); + } + + private static void InitializeSslContext( + SafeSslHandle handle, + Interop.AndroidCrypto.SSLReadCallback readCallback, + Interop.AndroidCrypto.SSLWriteCallback writeCallback, + SafeFreeSslCredentials credential, + SslAuthenticationOptions authOptions) + { + switch (credential.Policy) + { + case EncryptionPolicy.RequireEncryption: + case EncryptionPolicy.AllowNoEncryption: + break; + default: + throw new PlatformNotSupportedException(SR.Format(SR.net_encryptionpolicy_notsupported, credential.Policy)); + } + + bool isServer = authOptions.IsServer; + + if (authOptions.ApplicationProtocols != null + || authOptions.CipherSuitesPolicy != null + || (isServer && authOptions.RemoteCertRequired)) + { + // TODO: [AndroidCrypto] Handle non-system-default options + throw new NotImplementedException(nameof(SafeDeleteSslContext)); + } + + Interop.AndroidCrypto.SSLStreamInitialize(handle, isServer, readCallback, writeCallback, InitialBufferSize); + + if (credential.Protocols != SslProtocols.None) + {; + SslProtocols protocolsToEnable = credential.Protocols & s_supportedSslProtocols.Value; + if (protocolsToEnable == 0) + { + throw new PlatformNotSupportedException(SR.Format(SR.net_security_sslprotocol_notsupported, credential.Protocols)); + } + + (int minIndex, int maxIndex) = protocolsToEnable.ValidateContiguous(s_orderedSslProtocols); + Interop.AndroidCrypto.SSLStreamSetEnabledProtocols(handle, s_orderedSslProtocols.AsSpan(minIndex, maxIndex - minIndex + 1)); + } + + if (!isServer && !string.IsNullOrEmpty(authOptions.TargetHost)) + { + Interop.AndroidCrypto.SSLStreamConfigureParameters(handle, authOptions.TargetHost); + } + } } } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Managed/SslProtocolsValidation.cs b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Managed/SslProtocolsValidation.cs new file mode 100644 index 00000000000000..8343f88f2a603c --- /dev/null +++ b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Managed/SslProtocolsValidation.cs @@ -0,0 +1,75 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Security.Authentication; + +namespace System.Net +{ + internal static class SslProtocolsValidation + { + public static (int MinIndex, int MaxIndex) ValidateContiguous(this SslProtocols protocols, SslProtocols[] orderedSslProtocols) + { + // A contiguous range of protocols is required. Find the min and max of the range, + // or throw if it's non-contiguous or if no protocols are specified. + + // First, mark all of the specified protocols. + Span protocolSet = stackalloc bool[orderedSslProtocols.Length]; + for (int i = 0; i < orderedSslProtocols.Length; i++) + { + protocolSet[i] = (protocols & orderedSslProtocols[i]) != 0; + } + + int minIndex = -1; + int maxIndex = -1; + + // Loop through them, starting from the lowest. + for (int min = 0; min < protocolSet.Length; min++) + { + if (protocolSet[min]) + { + // We found the first one that's set; that's the bottom of the range. + minIndex = min; + + // Now loop from there to look for the max of the range. + for (int max = min + 1; max < protocolSet.Length; max++) + { + if (!protocolSet[max]) + { + // We found the first one after the min that's not set; the top of the range + // is the one before this (which might be the same as the min). + maxIndex = max - 1; + + // Finally, verify that nothing beyond this one is set, as that would be + // a discontiguous set of protocols. + for (int verifyNotSet = max + 1; verifyNotSet < protocolSet.Length; verifyNotSet++) + { + if (protocolSet[verifyNotSet]) + { + throw new PlatformNotSupportedException(SR.Format(SR.net_security_sslprotocol_contiguous, protocols)); + } + } + + break; + } + } + + break; + } + } + + // If no protocols were set, throw. + if (minIndex == -1) + { + throw new PlatformNotSupportedException(SR.net_securityprotocolnotsupported); + } + + // If we didn't find an unset protocol after the min, go all the way to the last one. + if (maxIndex == -1) + { + maxIndex = orderedSslProtocols.Length - 1; + } + + return (minIndex, maxIndex); + } + } +} diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs index 44fc356c33668a..44c8757c51e82a 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs @@ -272,68 +272,11 @@ internal int ReadPendingWrites(byte[] buf, int offset, int count) private static void SetProtocols(SafeSslHandle sslContext, SslProtocols protocols) { - // A contiguous range of protocols is required. Find the min and max of the range, - // or throw if it's non-contiguous or if no protocols are specified. + (int minIndex, int maxIndex) = protocols.ValidateContiguous(s_orderedSslProtocols); + SslProtocols minProtocolId = s_orderedSslProtocols[minIndex]; + SslProtocols maxProtocolId = s_orderedSslProtocols[maxIndex]; - // First, mark all of the specified protocols. - SslProtocols[] orderedSslProtocols = s_orderedSslProtocols; - Span protocolSet = stackalloc bool[orderedSslProtocols.Length]; - for (int i = 0; i < orderedSslProtocols.Length; i++) - { - protocolSet[i] = (protocols & orderedSslProtocols[i]) != 0; - } - - SslProtocols minProtocolId = (SslProtocols)(-1); - SslProtocols maxProtocolId = (SslProtocols)(-1); - - // Loop through them, starting from the lowest. - for (int min = 0; min < protocolSet.Length; min++) - { - if (protocolSet[min]) - { - // We found the first one that's set; that's the bottom of the range. - minProtocolId = orderedSslProtocols[min]; - - // Now loop from there to look for the max of the range. - for (int max = min + 1; max < protocolSet.Length; max++) - { - if (!protocolSet[max]) - { - // We found the first one after the min that's not set; the top of the range - // is the one before this (which might be the same as the min). - maxProtocolId = orderedSslProtocols[max - 1]; - - // Finally, verify that nothing beyond this one is set, as that would be - // a discontiguous set of protocols. - for (int verifyNotSet = max + 1; verifyNotSet < protocolSet.Length; verifyNotSet++) - { - if (protocolSet[verifyNotSet]) - { - throw new PlatformNotSupportedException(SR.Format(SR.net_security_sslprotocol_contiguous, protocols)); - } - } - - break; - } - } - - break; - } - } - - // If no protocols were set, throw. - if (minProtocolId == (SslProtocols)(-1)) - { - throw new PlatformNotSupportedException(SR.net_securityprotocolnotsupported); - } - - // If we didn't find an unset protocol after the min, go all the way to the last one. - if (maxProtocolId == (SslProtocols)(-1)) - { - maxProtocolId = orderedSslProtocols[orderedSslProtocols.Length - 1]; - } - - // Finally set this min and max. + // Set the min and max. Interop.AppleCrypto.SslSetMinProtocolVersion(sslContext, minProtocolId); Interop.AppleCrypto.SslSetMaxProtocolVersion(sslContext, maxProtocolId); } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.Android.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.Android.cs index 1d468dba8a5743..46024d39abe94c 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.Android.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.Android.cs @@ -11,7 +11,23 @@ internal sealed partial class SslConnectionInfo { public SslConnectionInfo(SafeSslHandle sslContext) { - throw new NotImplementedException(nameof(SslConnectionInfo)); + string protocolString = Interop.AndroidCrypto.SSLStreamGetProtocol(sslContext); + SslProtocols protocol = protocolString switch + { +#pragma warning disable 0618 // 'SslProtocols.Ssl3' is obsolete + "SSLv3" => SslProtocols.Ssl3, +#pragma warning restore + "TLSv1" => SslProtocols.Tls, + "TLSv1.1" => SslProtocols.Tls11, + "TLSv1.2" => SslProtocols.Tls12, + "TLSv1.3" => SslProtocols.Tls13, + _ => SslProtocols.None, + }; + Protocol = (int)protocol; + + // Enum value names should match the cipher suite name, so we just parse the + string cipherSuite = Interop.AndroidCrypto.SSLStreamGetCipherSuite(sslContext); + MapCipherSuite(Enum.Parse(cipherSuite)); } } } 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 6245f00ae4570a..5890e1ccc14618 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 @@ -1,12 +1,13 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Buffers; using System.Diagnostics; using System.Security.Authentication; using System.Security.Authentication.ExtendedProtection; using System.Security.Cryptography.X509Certificates; +using PAL_SSLStreamStatus = Interop.AndroidCrypto.PAL_SSLStreamStatus; + namespace System.Net.Security { internal static class SslStreamPal @@ -58,7 +59,7 @@ public static SafeFreeCredentials AcquireCredentialsHandle( if (context == null) return null; - throw new NotImplementedException(nameof(GetNegotiatedApplicationProtocol)); + return Interop.AndroidCrypto.SSLStreamGetApplicationProtocol(((SafeDeleteSslContext)context).SslContext); } public static SecurityStatusPal EncryptMessage( @@ -72,7 +73,37 @@ public static SecurityStatusPal EncryptMessage( resultSize = 0; Debug.Assert(input.Length > 0, $"{nameof(input.Length)} > 0 since {nameof(CanEncryptEmptyMessage)} is false"); - throw new NotImplementedException(nameof(EncryptMessage)); + try + { + SafeDeleteSslContext sslContext = (SafeDeleteSslContext)securityContext; + SafeSslHandle sslHandle = sslContext.SslContext; + + PAL_SSLStreamStatus ret = Interop.AndroidCrypto.SSLStreamWrite(sslHandle, input); + SecurityStatusPalErrorCode statusCode = ret switch + { + PAL_SSLStreamStatus.OK => SecurityStatusPalErrorCode.OK, + PAL_SSLStreamStatus.NeedData => SecurityStatusPalErrorCode.ContinueNeeded, + PAL_SSLStreamStatus.Renegotiate => SecurityStatusPalErrorCode.Renegotiate, + PAL_SSLStreamStatus.Closed => SecurityStatusPalErrorCode.ContextExpired, + _ => SecurityStatusPalErrorCode.InternalError + }; + + if (sslContext.BytesReadyForConnection <= output?.Length) + { + resultSize = sslContext.ReadPendingWrites(output, 0, output.Length); + } + else + { + output = sslContext.ReadPendingWrites()!; + resultSize = output.Length; + } + + return new SecurityStatusPal(statusCode); + } + catch (Exception e) + { + return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, e); + } } public static SecurityStatusPal DecryptMessage( @@ -81,7 +112,34 @@ public static SecurityStatusPal DecryptMessage( ref int offset, ref int count) { - throw new NotImplementedException(nameof(DecryptMessage)); + try + { + SafeDeleteSslContext sslContext = (SafeDeleteSslContext)securityContext; + SafeSslHandle sslHandle = sslContext.SslContext; + + sslContext.Write(buffer.AsSpan(offset, count)); + + PAL_SSLStreamStatus ret = Interop.AndroidCrypto.SSLStreamRead(sslHandle, buffer.AsSpan(offset, count), out int read); + if (ret == PAL_SSLStreamStatus.Error) + return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError); + + count = read; + + SecurityStatusPalErrorCode statusCode = ret switch + { + PAL_SSLStreamStatus.OK => SecurityStatusPalErrorCode.OK, + PAL_SSLStreamStatus.NeedData => SecurityStatusPalErrorCode.OK, + PAL_SSLStreamStatus.Renegotiate => SecurityStatusPalErrorCode.Renegotiate, + PAL_SSLStreamStatus.Closed => SecurityStatusPalErrorCode.ContextExpired, + _ => SecurityStatusPalErrorCode.InternalError + }; + + return new SecurityStatusPal(statusCode); + } + catch (Exception e) + { + return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, e); + } } public static ChannelBinding? QueryContextChannelBinding( @@ -134,12 +192,17 @@ private static SecurityStatusPal HandshakeInternal( SafeSslHandle sslHandle = sslContext!.SslContext; - // Do handshake - // Interop.AndroidCrypto.SSLStreamHandshake + PAL_SSLStreamStatus ret = Interop.AndroidCrypto.SSLStreamHandshake(sslHandle); + SecurityStatusPalErrorCode statusCode = ret switch + { + PAL_SSLStreamStatus.OK => SecurityStatusPalErrorCode.OK, + PAL_SSLStreamStatus.NeedData => SecurityStatusPalErrorCode.ContinueNeeded, + _ => SecurityStatusPalErrorCode.InternalError + }; outputBuffer = sslContext.ReadPendingWrites(); - return new SecurityStatusPal(SecurityStatusPalErrorCode.OK); + return new SecurityStatusPal(statusCode); } catch (Exception exc) { @@ -166,8 +229,7 @@ public static SecurityStatusPal ApplyShutdownToken( SafeSslHandle sslHandle = sslContext.SslContext; - // bool success = Interop.AndroidCrypto.SslShutdown(sslHandle); - bool success = true; + bool success = Interop.AndroidCrypto.SSLStreamShutdown(sslHandle); if (success) { return new SecurityStatusPal(SecurityStatusPalErrorCode.OK); diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/AssemblyInfo.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/AssemblyInfo.cs index 4b859917fd8cb2..667ca1ca5459c8 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/AssemblyInfo.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/AssemblyInfo.cs @@ -4,4 +4,4 @@ using Xunit; [assembly: ActiveIssue("https://github.com/dotnet/runtime/issues/34690", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] -[assembly: SkipOnMono("System.Net.Security is not supported on Browser", TestPlatforms.Browser)] \ No newline at end of file +[assembly: SkipOnPlatform(TestPlatforms.Browser, "System.Net.Security is not supported on Browser")] \ No newline at end of file diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/TestConfiguration.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/TestConfiguration.cs index ecfba44a860a48..a7dbdc9451ea4b 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/TestConfiguration.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/TestConfiguration.cs @@ -41,8 +41,8 @@ public static Task WhenAllOrAnyFailedWithTimeout(params Task[] tasks) return true; } - // On macOS, the null cipher (no encryption) is not supported. - if (OperatingSystem.IsMacOS()) + // On macOS and Android, the null cipher (no encryption) is not supported. + if (OperatingSystem.IsMacOS() || OperatingSystem.IsAndroid()) { return false; } diff --git a/src/libraries/System.Net.Security/tests/UnitTests/AssemblyInfo.cs b/src/libraries/System.Net.Security/tests/UnitTests/AssemblyInfo.cs index 766d39919ff4eb..ea25dc5f7ade53 100644 --- a/src/libraries/System.Net.Security/tests/UnitTests/AssemblyInfo.cs +++ b/src/libraries/System.Net.Security/tests/UnitTests/AssemblyInfo.cs @@ -3,4 +3,4 @@ using Xunit; -[assembly: SkipOnMono("System.Net.Security is not supported on Browser", TestPlatforms.Browser)] \ No newline at end of file +[assembly: SkipOnPlatform(TestPlatforms.Browser, "System.Net.Security is not supported on Browser")] \ No newline at end of file diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs index bf57c9311602c8..7e4baeda1985d1 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs @@ -1480,7 +1480,8 @@ public SocketError Connect(byte[] socketAddress, int socketAddressLen) SocketError errorCode; int observedSequenceNumber; _sendQueue.IsReady(this, out observedSequenceNumber); - if (SocketPal.TryStartConnect(_socket, socketAddress, socketAddressLen, out errorCode)) + if (SocketPal.TryStartConnect(_socket, socketAddress, socketAddressLen, out errorCode) || + !ShouldRetrySyncOperation(out errorCode)) { _socket.RegisterConnectResult(errorCode); return errorCode; diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/AssemblyInfo.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/AssemblyInfo.cs index 4c89f7536b3fa1..2df041cdba5950 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/AssemblyInfo.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/AssemblyInfo.cs @@ -5,4 +5,4 @@ [assembly: SkipOnCoreClr("System.Net.Tests are flaky and/or long running: https://github.com/dotnet/runtime/issues/131", RuntimeConfiguration.Checked)] [assembly: ActiveIssue("https://github.com/dotnet/runtime/issues/34690", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] -[assembly: SkipOnMono("System.Net.Sockets is not supported on Browser", TestPlatforms.Browser)] +[assembly: SkipOnPlatform(TestPlatforms.Browser, "System.Net.Sockets is not supported on Browser")] diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs index 67720af567ac82..d31cf2d43c4789 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs @@ -123,7 +123,7 @@ public async Task Connect_AfterDisconnect_Fails() } [OuterLoop("Connects to external server")] - [PlatformSpecific(~(TestPlatforms.OSX | TestPlatforms.FreeBSD))] // Not supported on BSD like OSes. + [SkipOnPlatform(TestPlatforms.OSX | TestPlatforms.FreeBSD, "Not supported on BSD like OSes.")] [Theory] [InlineData("1.1.1.1", false)] [InlineData("1.1.1.1", true)] diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/CreateSocketTests.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/CreateSocketTests.cs index c678cb3431bf0f..230495629c4173 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/CreateSocketTests.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/CreateSocketTests.cs @@ -217,7 +217,7 @@ public void CtorAndAccept_SocketNotKeptAliveViaInheritance(bool validateClientOu [Theory] [InlineData(AddressFamily.Packet)] [InlineData(AddressFamily.ControllerAreaNetwork)] - [PlatformSpecific(~TestPlatforms.Linux)] + [SkipOnPlatform(TestPlatforms.Linux, "Not supported on Linux.")] public void Ctor_Netcoreapp_Throws(AddressFamily addressFamily) { // All protocols are Linux specific and throw on other platforms diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/DualModeSocketTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/DualModeSocketTest.cs index d7d037abba193a..1921e10fa2f8ce 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/DualModeSocketTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/DualModeSocketTest.cs @@ -1364,7 +1364,7 @@ public void Socket_ReceiveFromV4IPEndPointFromV4Client_Throws() } [Fact] // Base case - [PlatformSpecific(~TestPlatforms.OSX)] // ReceiveFrom not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "ReceiveFrom not supported on OSX")] public void Socket_ReceiveFromDnsEndPoint_Throws() { // "The parameter remoteEP must not be of type DnsEndPoint." @@ -1380,28 +1380,28 @@ public void Socket_ReceiveFromDnsEndPoint_Throws() } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // ReceiveFrom not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "ReceiveFrom not supported on OSX")] public void ReceiveFromV4BoundToSpecificV4_Success() { ReceiveFrom_Helper(IPAddress.Loopback, IPAddress.Loopback); } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // ReceiveFrom not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "ReceiveFrom not supported on OSX")] public void ReceiveFromV4BoundToAnyV4_Success() { ReceiveFrom_Helper(IPAddress.Any, IPAddress.Loopback); } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // ReceiveFrom not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "ReceiveFrom not supported on OSX")] public void ReceiveFromV6BoundToSpecificV6_Success() { ReceiveFrom_Helper(IPAddress.IPv6Loopback, IPAddress.IPv6Loopback); } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // ReceiveFrom not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "ReceiveFrom not supported on OSX")] public void ReceiveFromV6BoundToAnyV6_Success() { ReceiveFrom_Helper(IPAddress.IPv6Any, IPAddress.IPv6Loopback); @@ -1444,7 +1444,7 @@ public void ReceiveFromV6BoundToAnyV4_NotReceived() } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // ReceiveFrom not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "ReceiveFrom not supported on OSX")] public void ReceiveFromV4BoundToAnyV6_Success() { ReceiveFrom_Helper(IPAddress.IPv6Any, IPAddress.Loopback); @@ -1495,7 +1495,7 @@ public void Socket_BeginReceiveFromV4IPEndPointFromV4Client_Throws() } [Fact] // Base case - [PlatformSpecific(~TestPlatforms.OSX)] // BeginReceiveFrom not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "BeginReceiveFrom not supported on OSX")] // "The parameter remoteEP must not be of type DnsEndPoint." public void Socket_BeginReceiveFromDnsEndPoint_Throws() { @@ -1512,28 +1512,28 @@ public void Socket_BeginReceiveFromDnsEndPoint_Throws() } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // BeginReceiveFrom not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "BeginReceiveFrom not supported on OSX")] public void BeginReceiveFromV4BoundToSpecificV4_Success() { BeginReceiveFrom_Helper(IPAddress.Loopback, IPAddress.Loopback); } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // BeginReceiveFrom not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "BeginReceiveFrom not supported on OSX")] public void BeginReceiveFromV4BoundToAnyV4_Success() { BeginReceiveFrom_Helper(IPAddress.Any, IPAddress.Loopback); } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // BeginReceiveFrom not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "BeginReceiveFrom not supported on OSX")] public void BeginReceiveFromV6BoundToSpecificV6_Success() { BeginReceiveFrom_Helper(IPAddress.IPv6Loopback, IPAddress.IPv6Loopback); } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // BeginReceiveFrom not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "BeginReceiveFrom not supported on OSX")] public void BeginReceiveFromV6BoundToAnyV6_Success() { BeginReceiveFrom_Helper(IPAddress.IPv6Any, IPAddress.IPv6Loopback); @@ -1576,7 +1576,7 @@ public void BeginReceiveFromV6BoundToAnyV4_NotReceived() } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // BeginReceiveFrom not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "BeginReceiveFrom not supported on OSX")] public void BeginReceiveFromV4BoundToAnyV6_Success() { BeginReceiveFrom_Helper(IPAddress.IPv6Any, IPAddress.Loopback); @@ -1645,7 +1645,7 @@ public void Socket_ReceiveFromAsyncV4IPEndPointFromV4Client_Throws() } [Fact] // Base case - [PlatformSpecific(~TestPlatforms.OSX)] // ReceiveFromAsync not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "ReceiveFromAsync not supported on OSX")] // "The parameter remoteEP must not be of type DnsEndPoint." public void Socket_ReceiveFromAsyncDnsEndPoint_Throws() { @@ -1664,35 +1664,35 @@ public void Socket_ReceiveFromAsyncDnsEndPoint_Throws() } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // ReceiveFromAsync not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "ReceiveFromAsync not supported on OSX")] public void ReceiveFromAsyncV4BoundToSpecificV4_Success() { ReceiveFromAsync_Helper(IPAddress.Loopback, IPAddress.Loopback); } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // ReceiveFromAsync not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "ReceiveFromAsync not supported on OSX")] public void ReceiveFromAsyncV4BoundToAnyV4_Success() { ReceiveFromAsync_Helper(IPAddress.Any, IPAddress.Loopback); } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // ReceiveFromAsync not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "ReceiveFromAsync not supported on OSX")] public void ReceiveFromAsyncV6BoundToSpecificV6_Success() { ReceiveFromAsync_Helper(IPAddress.IPv6Loopback, IPAddress.IPv6Loopback); } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // ReceiveFromAsync not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "ReceiveFromAsync not supported on OSX")] public void ReceiveFromAsyncV6BoundToAnyV6_Success() { ReceiveFromAsync_Helper(IPAddress.IPv6Any, IPAddress.IPv6Loopback); } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // ReceiveFromAsync not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "ReceiveFromAsync not supported on OSX")] public void ReceiveFromAsyncV6BoundToSpecificV4_NotReceived() { Assert.Throws(() => @@ -1702,7 +1702,7 @@ public void ReceiveFromAsyncV6BoundToSpecificV4_NotReceived() } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // ReceiveFromAsync not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "ReceiveFromAsync not supported on OSX")] public void ReceiveFromAsyncV4BoundToSpecificV6_NotReceived() { Assert.Throws(() => @@ -1712,7 +1712,7 @@ public void ReceiveFromAsyncV4BoundToSpecificV6_NotReceived() } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // ReceiveFromAsync not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "ReceiveFromAsync not supported on OSX")] public void ReceiveFromAsyncV6BoundToAnyV4_NotReceived() { Assert.Throws(() => @@ -1722,7 +1722,7 @@ public void ReceiveFromAsyncV6BoundToAnyV4_NotReceived() } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // ReceiveFromAsync not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "ReceiveFromAsync not supported on OSX")] public void ReceiveFromAsyncV4BoundToAnyV6_Success() { ReceiveFromAsync_Helper(IPAddress.IPv6Any, IPAddress.Loopback); @@ -1840,7 +1840,7 @@ public void Socket_ReceiveMessageFromV4IPEndPointFromV4Client_Throws() } [Fact] // Base case - [PlatformSpecific(~TestPlatforms.OSX)] // ReceiveMessageFrom not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "ReceiveMessageFrom not supported on OSX")] // "The parameter remoteEP must not be of type DnsEndPoint." public void Socket_ReceiveMessageFromDnsEndPoint_Throws() { @@ -1858,42 +1858,42 @@ public void Socket_ReceiveMessageFromDnsEndPoint_Throws() } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // ReceiveMessageFrom not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "ReceiveMessageFrom not supported on OSX")] public void ReceiveMessageFromV4BoundToSpecificMappedV4_Success() { ReceiveMessageFrom_Helper(IPAddress.Loopback.MapToIPv6(), IPAddress.Loopback); } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // ReceiveMessageFrom not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "ReceiveMessageFrom not supported on OSX")] public void ReceiveMessageFromV4BoundToAnyMappedV4_Success() { ReceiveMessageFrom_Helper(IPAddress.Any.MapToIPv6(), IPAddress.Loopback); } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // ReceiveMessageFrom not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "ReceiveMessageFrom not supported on OSX")] public void ReceiveMessageFromV4BoundToSpecificV4_Success() { ReceiveMessageFrom_Helper(IPAddress.Loopback, IPAddress.Loopback); } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // ReceiveMessageFrom not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "ReceiveMessageFrom not supported on OSX")] public void ReceiveMessageFromV4BoundToAnyV4_Success() { ReceiveMessageFrom_Helper(IPAddress.Any, IPAddress.Loopback); } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // ReceiveMessageFrom not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "ReceiveMessageFrom not supported on OSX")] public void ReceiveMessageFromV6BoundToSpecificV6_Success() { ReceiveMessageFrom_Helper(IPAddress.IPv6Loopback, IPAddress.IPv6Loopback); } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // ReceiveMessageFrom not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "ReceiveMessageFrom not supported on OSX")] public void ReceiveMessageFromV6BoundToAnyV6_Success() { ReceiveMessageFrom_Helper(IPAddress.IPv6Any, IPAddress.IPv6Loopback); @@ -1930,7 +1930,7 @@ public void ReceiveMessageFromV6BoundToAnyV4_NotReceived() } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // ReceiveMessageFrom not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "ReceiveMessageFrom not supported on OSX")] public void ReceiveMessageFromV4BoundToAnyV6_Success() { ReceiveMessageFrom_Helper(IPAddress.IPv6Any, IPAddress.Loopback); @@ -1939,7 +1939,7 @@ public void ReceiveMessageFromV4BoundToAnyV6_Success() [Theory] [InlineData(true)] [InlineData(false)] - [PlatformSpecific(~TestPlatforms.OSX)] // ReceiveMessageFrom not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "ReceiveMessageFrom not supported on OSX")] public void ReceiveMessageFromAsync_SocketAsyncEventArgs_Success(bool ipv4) { const int DataLength = 10; @@ -2064,7 +2064,7 @@ public void Socket_BeginReceiveMessageFromV4IPEndPointFromV4Client_Throws() } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // BeginReceiveMessageFrom not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "BeginReceiveMessageFrom not supported on OSX")] // "The parameter remoteEP must not be of type DnsEndPoint." public void Socket_BeginReceiveMessageFromDnsEndPoint_Throws() { @@ -2082,49 +2082,49 @@ public void Socket_BeginReceiveMessageFromDnsEndPoint_Throws() } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // BeginReceiveMessageFrom not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "BeginReceiveMessageFrom not supported on OSX")] public void BeginReceiveMessageFromV4BoundToSpecificMappedV4_Success() { BeginReceiveMessageFrom_Helper(IPAddress.Loopback.MapToIPv6(), IPAddress.Loopback); } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // BeginReceiveMessageFrom not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "BeginReceiveMessageFrom not supported on OSX")] public void BeginReceiveMessageFromV4BoundToAnyMappedV4_Success() { BeginReceiveMessageFrom_Helper(IPAddress.Any.MapToIPv6(), IPAddress.Loopback); } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // BeginReceiveMessageFrom not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "BeginReceiveMessageFrom not supported on OSX")] public void BeginReceiveMessageFromV4BoundToSpecificV4_Success() { BeginReceiveMessageFrom_Helper(IPAddress.Loopback, IPAddress.Loopback); } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // BeginReceiveMessageFrom not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "BeginReceiveMessageFrom not supported on OSX")] public void BeginReceiveMessageFromV4BoundToAnyV4_Success() { BeginReceiveMessageFrom_Helper(IPAddress.Any, IPAddress.Loopback); } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // BeginReceiveMessageFrom not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "BeginReceiveMessageFrom not supported on OSX")] public void BeginReceiveMessageFromV6BoundToSpecificV6_Success() { BeginReceiveMessageFrom_Helper(IPAddress.IPv6Loopback, IPAddress.IPv6Loopback); } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // BeginReceiveMessageFrom not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "BeginReceiveMessageFrom not supported on OSX")] public void BeginReceiveMessageFromV6BoundToAnyV6_Success() { BeginReceiveMessageFrom_Helper(IPAddress.IPv6Any, IPAddress.IPv6Loopback); } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // BeginReceiveMessageFrom not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "BeginReceiveMessageFrom not supported on OSX")] public void BeginReceiveMessageFromV6BoundToSpecificV4_NotReceived() { Assert.Throws(() => @@ -2134,7 +2134,7 @@ public void BeginReceiveMessageFromV6BoundToSpecificV4_NotReceived() } [Fact] - [PlatformSpecific(~(TestPlatforms.Linux | TestPlatforms.OSX))] // Expected behavior is different on OSX and Linux + [SkipOnPlatform(TestPlatforms.Linux | TestPlatforms.OSX, "Expected behavior is different on OSX and Linux")] public void BeginReceiveMessageFromV4BoundToSpecificV6_NotReceived() { Assert.Throws(() => @@ -2161,7 +2161,7 @@ public void BeginReceiveMessageFromV4BoundToSpecificV6_NotReceived_Linux() } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // BeginReceiveMessageFrom not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "BeginReceiveMessageFrom not supported on OSX")] public void BeginReceiveMessageFromV6BoundToAnyV4_NotReceived() { Assert.Throws(() => @@ -2171,7 +2171,7 @@ public void BeginReceiveMessageFromV6BoundToAnyV4_NotReceived() } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // BeginReceiveMessageFrom not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "BeginReceiveMessageFrom not supported on OSX")] public void BeginReceiveMessageFromV4BoundToAnyV6_Success() { BeginReceiveMessageFrom_Helper(IPAddress.IPv6Any, IPAddress.Loopback); @@ -2235,7 +2235,7 @@ public void Socket_ReceiveMessageFromAsyncV4IPEndPointFromV4Client_Throws() } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // ReceiveMessageFromAsync not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "ReceiveMessageFromAsync not supported on OSX")] // "The parameter remoteEP must not be of type DnsEndPoint." public void Socket_ReceiveMessageFromAsyncDnsEndPoint_Throws() { @@ -2255,49 +2255,49 @@ public void Socket_ReceiveMessageFromAsyncDnsEndPoint_Throws() } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // ReceiveMessageFromAsync not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "ReceiveMessageFromAsync not supported on OSX")] public void ReceiveMessageFromAsyncV4BoundToSpecificMappedV4_Success() { ReceiveMessageFromAsync_Helper(IPAddress.Loopback.MapToIPv6(), IPAddress.Loopback); } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // ReceiveMessageFromAsync not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "ReceiveMessageFromAsync not supported on OSX")] public void ReceiveMessageFromAsyncV4BoundToAnyMappedV4_Success() { ReceiveMessageFromAsync_Helper(IPAddress.Any.MapToIPv6(), IPAddress.Loopback); } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // ReceiveMessageFromAsync not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "ReceiveMessageFromAsync not supported on OSX")] public void ReceiveMessageFromAsyncV4BoundToSpecificV4_Success() { ReceiveMessageFromAsync_Helper(IPAddress.Loopback, IPAddress.Loopback); } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // ReceiveMessageFromAsync not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "ReceiveMessageFromAsync not supported on OSX")] public void ReceiveMessageFromAsyncV4BoundToAnyV4_Success() { ReceiveMessageFromAsync_Helper(IPAddress.Any, IPAddress.Loopback); } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // ReceiveMessageFromAsync not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "ReceiveMessageFromAsync not supported on OSX")] public void ReceiveMessageFromAsyncV6BoundToSpecificV6_Success() { ReceiveMessageFromAsync_Helper(IPAddress.IPv6Loopback, IPAddress.IPv6Loopback); } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // ReceiveMessageFromAsync not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "ReceiveMessageFromAsync not supported on OSX")] public void ReceiveMessageFromAsyncV6BoundToAnyV6_Success() { ReceiveMessageFromAsync_Helper(IPAddress.IPv6Any, IPAddress.IPv6Loopback); } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // ReceiveMessageFromAsync not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "ReceiveMessageFromAsync not supported on OSX")] public void ReceiveMessageFromAsyncV6BoundToSpecificV4_NotReceived() { Assert.Throws(() => @@ -2307,7 +2307,7 @@ public void ReceiveMessageFromAsyncV6BoundToSpecificV4_NotReceived() } [Fact] - [PlatformSpecific(~(TestPlatforms.Linux | TestPlatforms.OSX))] // Expected behavior is different on OSX and Linux + [SkipOnPlatform(TestPlatforms.Linux | TestPlatforms.OSX, "Expected behavior is different on OSX and Linux")] public void ReceiveMessageFromAsyncV4BoundToSpecificV6_NotReceived() { Assert.Throws(() => @@ -2334,7 +2334,7 @@ public void ReceiveMessageFromAsyncV4BoundToSpecificV6_NotReceived_Linux() } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // ReceiveMessageFromAsync not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "ReceiveMessageFromAsync not supported on OSX")] public void ReceiveMessageFromAsyncV6BoundToAnyV4_NotReceived() { Assert.Throws(() => @@ -2344,7 +2344,7 @@ public void ReceiveMessageFromAsyncV6BoundToAnyV4_NotReceived() } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // ReceiveMessageFromAsync not supported on OSX + [SkipOnPlatform(TestPlatforms.OSX, "ReceiveMessageFromAsync not supported on OSX")] public void ReceiveMessageFromAsyncV4BoundToAnyV6_Success() { ReceiveMessageFromAsync_Helper(IPAddress.IPv6Any, IPAddress.Loopback); diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/LingerStateTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/LingerStateTest.cs index b843110a72eae1..053fbc7acd20b5 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/LingerStateTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/LingerStateTest.cs @@ -43,7 +43,7 @@ public void Socket_LingerState_Common_Boundaries_CorrectBehavior() [OuterLoop] [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // The upper bound for linger time is drastically different on OS X. + [SkipOnPlatform(TestPlatforms.OSX, "The upper bound for linger time is drastically different on OS X.")] public void Socket_LingerState_Upper_Boundaries_CorrectBehavior() { Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SelectTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SelectTest.cs index 3f3dd22492b9fc..5a7edea1ccd75b 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SelectTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SelectTest.cs @@ -23,7 +23,7 @@ public SelectTest(ITestOutputHelper output) private const int SmallTimeoutMicroseconds = 10 * 1000; private const int FailTimeoutMicroseconds = 30 * 1000 * 1000; - [PlatformSpecific(~TestPlatforms.OSX)] // typical OSX install has very low max open file descriptors value + [SkipOnPlatform(TestPlatforms.OSX, "typical OSX install has very low max open file descriptors value")] [Theory] [InlineData(90, 0)] [InlineData(0, 90)] @@ -109,7 +109,7 @@ public void Select_SocketAlreadyClosed_AllSocketsClosableAfterException(int sock } } - [PlatformSpecific(~TestPlatforms.OSX)] // typical OSX install has very low max open file descriptors value + [SkipOnPlatform(TestPlatforms.OSX, "typical OSX install has very low max open file descriptors value")] [Fact] public void Select_ReadError_NoneReady_ManySockets() { @@ -142,7 +142,7 @@ public void Select_ReadError_NoneReady(int reads, int errors) } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // typical OSX install has very low max open file descriptors value + [SkipOnPlatform(TestPlatforms.OSX, "typical OSX install has very low max open file descriptors value")] public void Select_Read_OneReadyAtATime_ManySockets() { Select_Read_OneReadyAtATime(90); // value larger than the internal value in SocketPal.Unix that swaps between stack and heap allocation @@ -176,7 +176,7 @@ public void Select_Read_OneReadyAtATime(int reads) } } - [PlatformSpecific(~TestPlatforms.OSX)] // typical OSX install has very low max open file descriptors value + [SkipOnPlatform(TestPlatforms.OSX, "typical OSX install has very low max open file descriptors value")] [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // [ActiveIssue("https://github.com/dotnet/runtime/issues/18258")] public void Select_Error_OneReadyAtATime() { diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceive.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceive.cs index 3d37cc502aeefc..41a1a8ae64f7ec 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceive.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceive.cs @@ -684,7 +684,7 @@ await Task.WhenAll( } [Fact] - [PlatformSpecific(~(TestPlatforms.OSX | TestPlatforms.FreeBSD))] // SendBufferSize, ReceiveBufferSize = 0 not supported on BSD like stacks. + [SkipOnPlatform(TestPlatforms.OSX | TestPlatforms.FreeBSD, "SendBufferSize, ReceiveBufferSize = 0 not supported on BSD like stacks.")] public async Task SendRecv_NoBuffering_Success() { if (UsesSync) return; @@ -862,7 +862,7 @@ error is SocketException || [Theory] [MemberData(nameof(UdpReceiveGetsCanceledByDispose_Data))] - [PlatformSpecific(~TestPlatforms.OSX)] // Not supported on OSX. + [SkipOnPlatform(TestPlatforms.OSX, "Not supported on OSX.")] public async Task UdpReceiveGetsCanceledByDispose(IPAddress address) { // We try this a couple of times to deal with a timing race: if the Dispose happens diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveMisc.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveMisc.cs index f9fcfcac88626d..453f3d34307c6f 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveMisc.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveMisc.cs @@ -191,7 +191,7 @@ public async Task ReceiveIovMaxUdp_SuccessOrMessageSize() } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // [ActiveIssue("https://github.com/dotnet/runtime/issues/18258")] - [PlatformSpecific(~TestPlatforms.Windows)] // All data is sent, even when very large (100M). + [SkipOnPlatform(TestPlatforms.Windows, "All data is sent, even when very large (100M).")] public void SocketSendWouldBlock_ReturnsBytesSent() { using (var server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketOptionNameTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketOptionNameTest.cs index c36e8c714cbc31..50cfe205b3ee48 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketOptionNameTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketOptionNameTest.cs @@ -121,7 +121,7 @@ public void MulticastInterface_Set_InvalidIndex_Throws() } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoNorServerCore))] // Skip on Nano: https://github.com/dotnet/runtime/issues/26286 - [PlatformSpecific(~(TestPlatforms.OSX | TestPlatforms.FreeBSD))] + [SkipOnPlatform(TestPlatforms.OSX | TestPlatforms.FreeBSD, "Expected behavior is different on OSX or FreeBSD")] public async Task MulticastInterface_Set_IPv6_AnyInterface_Succeeds() { if (PlatformDetection.IsRedHatFamily7) @@ -229,7 +229,7 @@ public void MulticastInterface_Set_IPv6_InvalidIndex_Throws() [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // In WSL, the connect() call fails immediately. [InlineData(false)] [InlineData(true)] - [PlatformSpecific(~TestPlatforms.FreeBSD)] // on FreeBSD Connect may or may not fail immediately based on timing. + [SkipOnPlatform(TestPlatforms.FreeBSD, "on FreeBSD Connect may or may not fail immediately based on timing.")] public void FailedConnect_GetSocketOption_SocketOptionNameError(bool simpleGet) { using (var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) { Blocking = false }) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs index 227eea351f2b31..e8dddf1fec8c7d 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs @@ -164,7 +164,7 @@ await listener.RunWithCallbackAsync(e => events.Enqueue((e, e.ActivityId)), asyn [OuterLoop] [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - [PlatformSpecific(~(TestPlatforms.OSX | TestPlatforms.FreeBSD))] // Same as Connect.ConnectGetsCanceledByDispose + [SkipOnPlatform(TestPlatforms.OSX | TestPlatforms.FreeBSD, "Same as Connect.ConnectGetsCanceledByDispose")] [MemberData(nameof(SocketMethods_WithBools_MemberData))] public void EventSource_SocketConnectFailure_LogsConnectFailed(string connectMethod, bool useDnsEndPoint) { diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs index e5ed27e9fe835c..b639f88d8f6385 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs @@ -255,7 +255,7 @@ public void Ttl_Roundtrips() } } - [PlatformSpecific(~(TestPlatforms.OSX | TestPlatforms.FreeBSD))] // BSD like doesn't have an equivalent of DontFragment + [SkipOnPlatform(TestPlatforms.OSX | TestPlatforms.FreeBSD, "BSD like doesn't have an equivalent of DontFragment")] [Fact] public void DontFragment_Roundtrips() { diff --git a/src/libraries/System.Net.WebClient/tests/AssemblyInfo.cs b/src/libraries/System.Net.WebClient/tests/AssemblyInfo.cs index 3b9910b90dcffc..d7fe854700dc17 100644 --- a/src/libraries/System.Net.WebClient/tests/AssemblyInfo.cs +++ b/src/libraries/System.Net.WebClient/tests/AssemblyInfo.cs @@ -4,4 +4,4 @@ using Xunit; [assembly: ActiveIssue("https://github.com/dotnet/runtime/issues/34690", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] -[assembly: SkipOnMono("System.Net.WebClient is not recommended for new development and not supported on wasm", TestPlatforms.Browser)] +[assembly: SkipOnPlatform(TestPlatforms.Browser, "System.Net.WebClient is not recommended for new development and not supported on Browser")] diff --git a/src/libraries/System.Net.WebClient/tests/System.Net.WebClient.Tests.csproj b/src/libraries/System.Net.WebClient/tests/System.Net.WebClient.Tests.csproj index fc248601812ef1..005d4bafc66fc7 100644 --- a/src/libraries/System.Net.WebClient/tests/System.Net.WebClient.Tests.csproj +++ b/src/libraries/System.Net.WebClient/tests/System.Net.WebClient.Tests.csproj @@ -3,6 +3,7 @@ $(NetCoreAppCurrent) $(DefineConstants);NETSTANDARD $(NoWarn);SYSLIB0014 + true diff --git a/src/libraries/System.Net.WebSockets.Client/tests/ClientWebSocketOptionsTests.cs b/src/libraries/System.Net.WebSockets.Client/tests/ClientWebSocketOptionsTests.cs index ed06f52994895c..736194fc0e717b 100644 --- a/src/libraries/System.Net.WebSockets.Client/tests/ClientWebSocketOptionsTests.cs +++ b/src/libraries/System.Net.WebSockets.Client/tests/ClientWebSocketOptionsTests.cs @@ -17,7 +17,7 @@ public class ClientWebSocketOptionsTests : ClientWebSocketTestBase public ClientWebSocketOptionsTests(ITestOutputHelper output) : base(output) { } [ConditionalFact(nameof(WebSocketsSupported))] - [PlatformSpecific(~TestPlatforms.Browser)] // Credentials not supported on browser + [SkipOnPlatform(TestPlatforms.Browser, "Credentials not supported on browser")] public static void UseDefaultCredentials_Roundtrips() { var cws = new ClientWebSocket(); @@ -29,7 +29,7 @@ public static void UseDefaultCredentials_Roundtrips() } [ConditionalFact(nameof(WebSocketsSupported))] - [PlatformSpecific(~TestPlatforms.Browser)] // Proxy not supported on browser + [SkipOnPlatform(TestPlatforms.Browser, "Proxy not supported on browser")] public static void Proxy_Roundtrips() { var cws = new ClientWebSocket(); @@ -102,7 +102,7 @@ public async Task Proxy_ConnectThruProxy_Success(Uri server) } [ConditionalFact(nameof(WebSocketsSupported))] - [PlatformSpecific(~TestPlatforms.Browser)] // Buffer not supported on browser + [SkipOnPlatform(TestPlatforms.Browser, "Buffer not supported on browser")] public static void SetBuffer_InvalidArgs_Throws() { // Recreate the minimum WebSocket buffer size values from the .NET Framework version of WebSocket, @@ -124,7 +124,7 @@ public static void SetBuffer_InvalidArgs_Throws() } [ConditionalFact(nameof(WebSocketsSupported))] - [PlatformSpecific(~TestPlatforms.Browser)] // KeepAlive not supported on browser + [SkipOnPlatform(TestPlatforms.Browser, "KeepAlive not supported on browser")] public static void KeepAliveInterval_Roundtrips() { var cws = new ClientWebSocket(); @@ -143,7 +143,7 @@ public static void KeepAliveInterval_Roundtrips() } [ConditionalFact(nameof(WebSocketsSupported))] - [PlatformSpecific(~TestPlatforms.Browser)] // Certificates not supported on browser + [SkipOnPlatform(TestPlatforms.Browser, "Certificates not supported on browser")] public void RemoteCertificateValidationCallback_Roundtrips() { using (var cws = new ClientWebSocket()) @@ -163,7 +163,7 @@ public void RemoteCertificateValidationCallback_Roundtrips() [ConditionalTheory(nameof(WebSocketsSupported))] [InlineData(false)] [InlineData(true)] - [PlatformSpecific(~TestPlatforms.Browser)] // Certificates not supported on browser + [SkipOnPlatform(TestPlatforms.Browser, "Certificates not supported on browser")] public async Task RemoteCertificateValidationCallback_PassedRemoteCertificateInfo(bool secure) { if (PlatformDetection.IsWindows7) @@ -200,7 +200,7 @@ await LoopbackServer.CreateClientAndServerAsync(async uri => [OuterLoop("Connects to remote service")] [ConditionalFact(nameof(WebSocketsSupported))] - [PlatformSpecific(~TestPlatforms.Browser)] // Credentials not supported on browser + [SkipOnPlatform(TestPlatforms.Browser, "Credentials not supported on browser")] public async Task ClientCertificates_ValidCertificate_ServerReceivesCertificateAndConnectAsyncSucceeds() { if (PlatformDetection.IsWindows7) @@ -238,7 +238,7 @@ await LoopbackServer.CreateClientAndServerAsync(async uri => [ActiveIssue("https://github.com/dotnet/runtime/issues/34690", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] [InlineData("ws://")] [InlineData("wss://")] - [PlatformSpecific(~TestPlatforms.Browser)] // Credentials not supported on browser + [SkipOnPlatform(TestPlatforms.Browser, "Credentials not supported on browser")] public async Task NonSecureConnect_ConnectThruProxy_CONNECTisUsed(string connectionType) { if (PlatformDetection.IsWindows7) diff --git a/src/libraries/System.Net.WebSockets.Client/tests/ConnectTest.cs b/src/libraries/System.Net.WebSockets.Client/tests/ConnectTest.cs index 1d644b4239de18..1f3f37f1a37103 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")] [ConditionalTheory(nameof(WebSocketsSupported)), MemberData(nameof(EchoHeadersServers))] - [PlatformSpecific(~TestPlatforms.Browser)] // CustomHeaders not supported on browser + [SkipOnPlatform(TestPlatforms.Browser, "CustomHeaders not supported on browser")] public async Task ConnectAsync_AddCustomHeaders_Success(Uri server) { using (var cws = new ClientWebSocket()) @@ -120,7 +120,7 @@ await LoopbackServer.CreateClientAndServerAsync(async uri => [OuterLoop("Uses external servers")] [ConditionalTheory(nameof(WebSocketsSupported)), MemberData(nameof(EchoHeadersServers))] - [PlatformSpecific(~TestPlatforms.Browser)] // Cookies not supported on browser + [SkipOnPlatform(TestPlatforms.Browser, "Cookies not supported on browser")] public async Task ConnectAsync_CookieHeaders_Success(Uri server) { using (var cws = new ClientWebSocket()) diff --git a/src/libraries/System.Net.WebSockets.Client/tests/KeepAliveTest.cs b/src/libraries/System.Net.WebSockets.Client/tests/KeepAliveTest.cs index 3ec136b0d63c0c..56acccdc05590a 100644 --- a/src/libraries/System.Net.WebSockets.Client/tests/KeepAliveTest.cs +++ b/src/libraries/System.Net.WebSockets.Client/tests/KeepAliveTest.cs @@ -11,7 +11,7 @@ namespace System.Net.WebSockets.Client.Tests { - [PlatformSpecific(~TestPlatforms.Browser)] // KeepAlive not supported on browser + [SkipOnPlatform(TestPlatforms.Browser, "KeepAlive not supported on browser")] public class KeepAliveTest : ClientWebSocketTestBase { public KeepAliveTest(ITestOutputHelper output) : base(output) { } diff --git a/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs b/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs index 787bed60c61c15..5d36a8c6c32bda 100644 --- a/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs +++ b/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs @@ -88,7 +88,7 @@ public async Task SendReceive_PartialMessageDueToSmallReceiveBuffer_Success(Uri [OuterLoop("Uses external servers")] [ConditionalTheory(nameof(WebSocketsSupported)), MemberData(nameof(EchoServers))] - [PlatformSpecific(~TestPlatforms.Browser)] // JS Websocket does not support see issue https://github.com/dotnet/runtime/issues/46983 + [SkipOnPlatform(TestPlatforms.Browser, "JS Websocket does not support see issue https://github.com/dotnet/runtime/issues/46983")] public async Task SendReceive_PartialMessageBeforeCompleteMessageArrives_Success(Uri server) { var sendBuffer = new byte[ushort.MaxValue + 1]; diff --git a/src/libraries/System.Net.WebSockets/tests/WebSocketCreateTest.cs b/src/libraries/System.Net.WebSockets/tests/WebSocketCreateTest.cs index 7f391f7754ec67..af0179ee5b58ca 100644 --- a/src/libraries/System.Net.WebSockets/tests/WebSocketCreateTest.cs +++ b/src/libraries/System.Net.WebSockets/tests/WebSocketCreateTest.cs @@ -40,7 +40,7 @@ public void CreateFromStream_ValidBufferSizes_CreatesWebSocket() [OuterLoop("Uses external servers")] [Theory] [MemberData(nameof(EchoServers))] - [PlatformSpecific(~TestPlatforms.Browser)] // System.Net.Sockets is not supported on this platform. + [SkipOnPlatform(TestPlatforms.Browser, "System.Net.Sockets is not supported on this platform.")] public async Task WebSocketProtocol_CreateFromConnectedStream_CanSendReceiveData(Uri echoUri) { if (PlatformDetection.IsWindows7) @@ -102,7 +102,7 @@ public async Task ReceiveAsync_UTF8SplitAcrossMultipleBuffers_ValidDataReceived( } [Theory] - [PlatformSpecific(~TestPlatforms.Browser)] // System.Net.Sockets is not supported on this platform. + [SkipOnPlatform(TestPlatforms.Browser, "System.Net.Sockets is not supported on this platform.")] [ActiveIssue("https://github.com/dotnet/runtime/issues/34690", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] [InlineData(0b_1000_0001, 0b_0_000_0001, false)] // fin + text, no mask + length == 1 [InlineData(0b_1100_0001, 0b_0_000_0001, true)] // fin + rsv1 + text, no mask + length == 1 @@ -149,7 +149,7 @@ public async Task ReceiveAsync_InvalidFrameHeader_AbortsAndThrowsException(byte } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] // System.Net.Sockets is not supported on this platform. + [SkipOnPlatform(TestPlatforms.Browser, "System.Net.Sockets is not supported on this platform.")] [ActiveIssue("https://github.com/dotnet/runtime/issues/34690", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public async Task ReceiveAsync_ServerSplitHeader_ValidDataReceived() { @@ -204,7 +204,7 @@ public async Task ReceiveAsync_ServerSplitHeader_ValidDataReceived() [OuterLoop("Uses external servers")] [Theory] [MemberData(nameof(EchoServersAndBoolean))] - [PlatformSpecific(~TestPlatforms.Browser)] // System.Net.Sockets is not supported on this platform. + [SkipOnPlatform(TestPlatforms.Browser, "System.Net.Sockets is not supported on this platform.")] public async Task WebSocketProtocol_CreateFromConnectedStream_CloseAsyncClosesStream(Uri echoUri, bool explicitCloseAsync) { if (PlatformDetection.IsWindows7) @@ -246,7 +246,7 @@ public async Task WebSocketProtocol_CreateFromConnectedStream_CloseAsyncClosesSt [OuterLoop("Uses external servers")] [Theory] [MemberData(nameof(EchoServersAndBoolean))] - [PlatformSpecific(~TestPlatforms.Browser)] // System.Net.Sockets is not supported on this platform. + [SkipOnPlatform(TestPlatforms.Browser, "System.Net.Sockets is not supported on this platform.")] public async Task WebSocketProtocol_CreateFromConnectedStream_CloseAsyncAfterCloseReceivedClosesStream(Uri echoUri, bool useCloseOutputAsync) { using (var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) diff --git a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.LinkAttributes.Shared.xml b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.LinkAttributes.Shared.xml index 8df05e609708b6..d910ad1eb64186 100644 --- a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.LinkAttributes.Shared.xml +++ b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.LinkAttributes.Shared.xml @@ -29,6 +29,11 @@ + + + + + diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index 807cca91a37ae5..2b214c82eaf720 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -3253,6 +3253,9 @@ The timeout must represent a value between -1 and Int32.MaxValue, inclusive. + + in {0}:token 0x{1:x}+0x{2:x} + in {0}:line {1} 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 9256e41f8a6332..c67c432163c73d 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 @@ -616,6 +616,7 @@ + @@ -1846,7 +1847,8 @@ - + + @@ -1923,6 +1925,10 @@ + + + diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/StackTrace.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/StackTrace.cs index 3c6ba961c79d30..10865d4432158e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/StackTrace.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/StackTrace.cs @@ -206,6 +206,7 @@ internal void ToString(TraceFormat traceFormat, StringBuilder sb) string word_At = SR.GetResourceString(nameof(SR.Word_At), defaultString: "at"); // We also want to pass in a default for inFileLineNumber. string inFileLineNum = SR.GetResourceString(nameof(SR.StackTrace_InFileLineNumber), defaultString: "in {0}:line {1}"); + string inFileILOffset = SR.GetResourceString(nameof(SR.StackTrace_InFileILOffset), defaultString: "in {0}:token 0x{1:x}+0x{2:x}"); bool fFirstFrame = true; for (int iFrameIndex = 0; iFrameIndex < _numOfFrames; iFrameIndex++) { @@ -323,6 +324,17 @@ internal void ToString(TraceFormat traceFormat, StringBuilder sb) sb.Append(' '); sb.AppendFormat(CultureInfo.InvariantCulture, inFileLineNum, fileName, sf.GetFileLineNumber()); } + else if (LocalAppContextSwitches.ShowILOffsets && mb.ReflectedType != null) + { + string assemblyName = mb.ReflectedType.Module.ScopeName; + try + { + int token = mb.MetadataToken; + sb.Append(' '); + sb.AppendFormat(CultureInfo.InvariantCulture, inFileILOffset, assemblyName, token, sf.GetILOffset()); + } + catch (System.InvalidOperationException) {} + } } // Skip EDI boundary for async diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/RuntimeEventSource.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/RuntimeEventSource.cs index 8b37a0b70cc115..ca51113474c980 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/RuntimeEventSource.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/RuntimeEventSource.cs @@ -62,8 +62,8 @@ protected override void OnEventCommand(EventCommandEventArgs command) // On disable, PollingCounters will stop polling for values so it should be fine to leave them around. _cpuTimeCounter ??= new PollingCounter("cpu-usage", this, () => RuntimeEventSourceHelper.GetCpuUsage()) { DisplayName = "CPU Usage", DisplayUnits = "%" }; - _workingSetCounter ??= new PollingCounter("working-set", this, () => (double)(Environment.WorkingSet / 1000000)) { DisplayName = "Working Set", DisplayUnits = "MB" }; - _gcHeapSizeCounter ??= new PollingCounter("gc-heap-size", this, () => (double)(GC.GetTotalMemory(false) / 1000000)) { DisplayName = "GC Heap Size", DisplayUnits = "MB" }; + _workingSetCounter ??= new PollingCounter("working-set", this, () => (double)(Environment.WorkingSet / 1_000_000)) { DisplayName = "Working Set", DisplayUnits = "MB" }; + _gcHeapSizeCounter ??= new PollingCounter("gc-heap-size", this, () => (double)(GC.GetTotalMemory(false) / 1_000_000)) { DisplayName = "GC Heap Size", DisplayUnits = "MB" }; _gen0GCCounter ??= new IncrementingPollingCounter("gen-0-gc-count", this, () => GC.CollectionCount(0)) { DisplayName = "Gen 0 GC Count", DisplayRateTimeScale = new TimeSpan(0, 1, 0) }; _gen1GCCounter ??= new IncrementingPollingCounter("gen-1-gc-count", this, () => GC.CollectionCount(1)) { DisplayName = "Gen 1 GC Count", DisplayRateTimeScale = new TimeSpan(0, 1, 0) }; _gen2GCCounter ??= new IncrementingPollingCounter("gen-2-gc-count", this, () => GC.CollectionCount(2)) { DisplayName = "Gen 2 GC Count", DisplayRateTimeScale = new TimeSpan(0, 1, 0) }; @@ -77,7 +77,7 @@ protected override void OnEventCommand(EventCommandEventArgs command) var gcInfo = GC.GetGCMemoryInfo(); return gcInfo.HeapSizeBytes != 0 ? gcInfo.FragmentedBytes * 100d / gcInfo.HeapSizeBytes : 0; }) { DisplayName = "GC Fragmentation", DisplayUnits = "%" }; - _committedCounter ??= new PollingCounter("gc-committed", this, () => GC.GetGCMemoryInfo().TotalCommittedBytes) { DisplayName = "GC Committed Bytes", DisplayUnits = "B" }; + _committedCounter ??= new PollingCounter("gc-committed", this, () => (double)(GC.GetGCMemoryInfo().TotalCommittedBytes / 1_000_000)) { DisplayName = "GC Committed Bytes", DisplayUnits = "MB" }; #if !MONO _exceptionCounter ??= new IncrementingPollingCounter("exception-count", this, () => Exception.GetExceptionCount()) { DisplayName = "Exception Count", DisplayRateTimeScale = new TimeSpan(0, 0, 1) }; _gcTimeCounter ??= new PollingCounter("time-in-gc", this, () => GC.GetLastGCPercentTimeInGC()) { DisplayName = "% Time in GC since last GC", DisplayUnits = "%" }; diff --git a/src/libraries/System.Private.CoreLib/src/System/Enum.cs b/src/libraries/System.Private.CoreLib/src/System/Enum.cs index bd2b4f71f18334..cb86c8353c2684 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Enum.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Enum.cs @@ -230,7 +230,7 @@ private static string ValueToHexString(object value) Span resultSpan = new Span(ref result.GetRawStringData(), result.Length); string name = names[foundItems[--foundItemsCount]]; - name.AsSpan().CopyTo(resultSpan); + name.CopyTo(resultSpan); resultSpan = resultSpan.Slice(name.Length); while (--foundItemsCount >= 0) { @@ -239,7 +239,7 @@ private static string ValueToHexString(object value) resultSpan = resultSpan.Slice(2); name = names[foundItems[foundItemsCount]]; - name.AsSpan().CopyTo(resultSpan); + name.CopyTo(resultSpan); resultSpan = resultSpan.Slice(name.Length); } Debug.Assert(resultSpan.IsEmpty); diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.OSVersion.MacCatalyst.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.OSVersion.MacCatalyst.cs new file mode 100644 index 00000000000000..609dddcf5922e6 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Environment.OSVersion.MacCatalyst.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System +{ + public static partial class Environment + { + private static OperatingSystem GetOSVersion() + { + Version version = new Version(Interop.Sys.iOSSupportVersion()); + return new OperatingSystem(PlatformID.Unix, version); + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Exception.cs b/src/libraries/System.Private.CoreLib/src/System/Exception.cs index cef527ee50d15c..a36983489dca06 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Exception.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Exception.cs @@ -176,7 +176,7 @@ public override string ToString() static void Write(string source, ref Span dest) { - source.AsSpan().CopyTo(dest); + source.CopyTo(dest); dest = dest.Slice(source.Length); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanFormat.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanFormat.cs index 1ed63ae31a7b52..01162580b7876f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanFormat.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanFormat.cs @@ -269,7 +269,7 @@ private static bool TryFormatStandard(TimeSpan value, StandardFormat format, str } else { - decimalSeparator.AsSpan().CopyTo(destination); + decimalSeparator.CopyTo(destination); idx += decimalSeparator.Length; } WriteDigits(fraction, destination.Slice(idx, fractionDigits)); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/BufferedFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/BufferedFileStreamStrategy.cs index 2b914e7bc0aec1..96094cf01e7aa3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/BufferedFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/BufferedFileStreamStrategy.cs @@ -602,7 +602,7 @@ private void WriteByteSlow(byte value) ClearReadBufferBeforeWrite(); EnsureBufferAllocated(); } - else if (_writePos == _bufferSize - 1) + else { FlushWrite(); } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.cs index d64677b48efe85..1ca2e563998caa 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.cs @@ -7,21 +7,7 @@ namespace System.IO.Strategies { internal static partial class FileStreamHelpers { - // It's enabled by default. We are going to change that once we fix #16354, #25905 and #24847. - internal static bool UseNet5CompatStrategy { get; } = GetNet5CompatFileStreamSetting(); - - private static bool GetNet5CompatFileStreamSetting() - { - if (AppContext.TryGetSwitch("System.IO.UseNet5CompatFileStream", out bool fileConfig)) - { - return fileConfig; - } - - string? envVar = Environment.GetEnvironmentVariable("DOTNET_SYSTEM_IO_USENET5COMPATFILESTREAM"); - return envVar is null - ? true // Net5Compat is currently enabled by default; - : bool.IsTrueStringIgnoreCase(envVar) || envVar.Equals("1"); - } + internal static bool UseNet5CompatStrategy { get; } = AppContextConfigHelper.GetBooleanConfig("System.IO.UseNet5CompatFileStream", "DOTNET_SYSTEM_IO_USENET5COMPATFILESTREAM"); internal static FileStreamStrategy ChooseStrategy(FileStream fileStream, SafeFileHandle handle, FileAccess access, FileShare share, int bufferSize, bool isAsync) => WrapIfDerivedType(fileStream, ChooseStrategyCore(handle, access, share, bufferSize, isAsync)); diff --git a/src/libraries/System.Private.CoreLib/src/System/LocalAppContextSwitches.cs b/src/libraries/System.Private.CoreLib/src/System/LocalAppContextSwitches.cs index d669bd4cccc4e1..ed450d74943690 100644 --- a/src/libraries/System.Private.CoreLib/src/System/LocalAppContextSwitches.cs +++ b/src/libraries/System.Private.CoreLib/src/System/LocalAppContextSwitches.cs @@ -47,5 +47,23 @@ public static bool SerializationGuard [MethodImpl(MethodImplOptions.AggressiveInlining)] get => GetCachedSwitchValue("Switch.System.Runtime.Serialization.SerializationGuard", ref s_serializationGuard); } + + private static int s_showILOffset; + private static bool GetDefaultShowILOffsetSetting() + { + if (s_showILOffset < 0) return false; + if (s_showILOffset > 0) return true; + + bool isSwitchEnabled = AppContextConfigHelper.GetBooleanConfig("Switch.System.Diagnostics.StackTrace.ShowILOffsets", true); + s_showILOffset = isSwitchEnabled ? 1 : -1; + + return isSwitchEnabled; + } + + public static bool ShowILOffsets + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => GetDefaultShowILOffsetSetting(); + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs b/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs index 7cf4fc606097bd..be13d587923653 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs @@ -758,7 +758,7 @@ private static bool TryCopyTo(string source, Span destination, out int cha { Debug.Assert(source != null); - if (source.AsSpan().TryCopyTo(destination)) + if (source.TryCopyTo(destination)) { charsWritten = source.Length; return true; diff --git a/src/libraries/System.Private.CoreLib/src/System/OperatingSystem.cs b/src/libraries/System.Private.CoreLib/src/System/OperatingSystem.cs index 02995ff47b74a6..892b31f8b9e819 100644 --- a/src/libraries/System.Private.CoreLib/src/System/OperatingSystem.cs +++ b/src/libraries/System.Private.CoreLib/src/System/OperatingSystem.cs @@ -203,7 +203,6 @@ public static bool IsMacOS() => public static bool IsMacOSVersionAtLeast(int major, int minor = 0, int build = 0) => IsMacOS() && IsOSVersionAtLeast(major, minor, build, 0); -/* Commented out for now, until we're ready to make changes to the public API /// /// Indicates whether the current application is running on Mac Catalyst. /// @@ -215,11 +214,10 @@ public static bool IsMacCatalyst() => #endif /// - /// Check for the Mac Catalyst version (returned by 'libobjc.get_operatingSystemVersion') with a >= version comparison. Used to guard APIs that were added in the given Mac Catalyst release. + /// Check for the Mac Catalyst version (iOS version as presented in Apple documentation) with a >= version comparison. Used to guard APIs that were added in the given Mac Catalyst release. /// public static bool IsMacCatalystVersionAtLeast(int major, int minor = 0, int build = 0) => IsMacCatalyst() && IsOSVersionAtLeast(major, minor, build, 0); -*/ /// /// Indicates whether the current application is running on tvOS. diff --git a/src/libraries/System.Private.CoreLib/src/System/Random.Xoshiro256StarStarImpl.cs b/src/libraries/System.Private.CoreLib/src/System/Random.Xoshiro256StarStarImpl.cs index 59bf7dab77c83c..8b4e14ebbc204c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Random.Xoshiro256StarStarImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Random.Xoshiro256StarStarImpl.cs @@ -112,7 +112,7 @@ public override int Next(int maxValue) public override int Next(int minValue, int maxValue) { - ulong exclusiveRange = (ulong)(maxValue - minValue); + ulong exclusiveRange = (ulong)((long)maxValue - minValue); if (exclusiveRange > 1) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Random.cs b/src/libraries/System.Private.CoreLib/src/System/Random.cs index 64b9eb97c09716..21ea60e442abfc 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Random.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Random.cs @@ -52,7 +52,12 @@ protected private Random(bool isThreadSafeRandom) /// Returns a non-negative random integer. /// A 32-bit signed integer that is greater than or equal to 0 and less than . - public virtual int Next() => _impl.Next(); + public virtual int Next() + { + int result = _impl.Next(); + AssertInRange(result, 0, int.MaxValue); + return result; + } /// Returns a non-negative random integer that is less than the specified maximum. /// The exclusive upper bound of the random number to be generated. must be greater than or equal to 0. @@ -68,7 +73,9 @@ public virtual int Next(int maxValue) ThrowMaxValueMustBeNonNegative(); } - return _impl.Next(maxValue); + int result = _impl.Next(maxValue); + AssertInRange(result, 0, maxValue); + return result; } /// Returns a random integer that is within a specified range. @@ -86,12 +93,19 @@ public virtual int Next(int minValue, int maxValue) ThrowMinMaxValueSwapped(); } - return _impl.Next(minValue, maxValue); + int result = _impl.Next(minValue, maxValue); + AssertInRange(result, minValue, maxValue); + return result; } /// Returns a non-negative random integer. /// A 64-bit signed integer that is greater than or equal to 0 and less than . - public virtual long NextInt64() => _impl.NextInt64(); + public virtual long NextInt64() + { + long result = _impl.NextInt64(); + AssertInRange(result, 0, long.MaxValue); + return result; + } /// Returns a non-negative random integer that is less than the specified maximum. /// The exclusive upper bound of the random number to be generated. must be greater than or equal to 0. @@ -107,7 +121,9 @@ public virtual long NextInt64(long maxValue) ThrowMaxValueMustBeNonNegative(); } - return _impl.NextInt64(maxValue); + long result = _impl.NextInt64(maxValue); + AssertInRange(result, 0, maxValue); + return result; } /// Returns a random integer that is within a specified range. @@ -125,16 +141,28 @@ public virtual long NextInt64(long minValue, long maxValue) ThrowMinMaxValueSwapped(); } - return _impl.NextInt64(minValue, maxValue); + long result = _impl.NextInt64(minValue, maxValue); + AssertInRange(result, minValue, maxValue); + return result; } /// Returns a random floating-point number that is greater than or equal to 0.0, and less than 1.0. /// A single-precision floating point number that is greater than or equal to 0.0, and less than 1.0. - public virtual float NextSingle() => _impl.NextSingle(); + public virtual float NextSingle() + { + float result = _impl.NextSingle(); + AssertInRange(result); + return result; + } /// Returns a random floating-point number that is greater than or equal to 0.0, and less than 1.0. /// A double-precision floating point number that is greater than or equal to 0.0, and less than 1.0. - public virtual double NextDouble() => _impl.NextDouble(); + public virtual double NextDouble() + { + double result = _impl.NextDouble(); + AssertInRange(result); + return result; + } /// Fills the elements of a specified array of bytes with random numbers. /// The array to be filled with random numbers. @@ -155,7 +183,12 @@ public virtual void NextBytes(byte[] buffer) /// Returns a random floating-point number between 0.0 and 1.0. /// A double-precision floating point number that is greater than or equal to 0.0, and less than 1.0. - protected virtual double Sample() => _impl.Sample(); + protected virtual double Sample() + { + double result = _impl.Sample(); + AssertInRange(result); + return result; + } private static void ThrowMaxValueMustBeNonNegative() => throw new ArgumentOutOfRangeException("maxValue", SR.Format(SR.ArgumentOutOfRange_NeedNonNegNum, "maxValue")); @@ -163,6 +196,23 @@ private static void ThrowMaxValueMustBeNonNegative() => private static void ThrowMinMaxValueSwapped() => throw new ArgumentOutOfRangeException("minValue", SR.Format(SR.Argument_MinMaxValue, "minValue", "maxValue")); + [Conditional("DEBUG")] + private static void AssertInRange(long result, long minInclusive, long maxExclusive) + { + if (maxExclusive > minInclusive) + { + Debug.Assert(result >= minInclusive && result < maxExclusive, $"Expected {minInclusive} <= {result} < {maxExclusive}"); + } + else + { + Debug.Assert(result == minInclusive, $"Expected {minInclusive} == {result}"); + } + } + + [Conditional("DEBUG")] + private static void AssertInRange(double result) => + Debug.Assert(result >= 0.0 && result < 1.0f, $"Expected 0.0 <= {result} < 1.0"); + /// Random implementation that delegates all calls to a ThreadStatic Impl instance. private sealed class ThreadSafeRandom : Random { @@ -189,7 +239,12 @@ public ThreadSafeRandom() : base(isThreadSafeRandom: true) { } [MethodImpl(MethodImplOptions.NoInlining)] private static XoshiroImpl Create() => t_random = new(); - public override int Next() => LocalRandom.Next(); + public override int Next() + { + int result = LocalRandom.Next(); + AssertInRange(result, 0, int.MaxValue); + return result; + } public override int Next(int maxValue) { @@ -198,7 +253,9 @@ public override int Next(int maxValue) ThrowMaxValueMustBeNonNegative(); } - return LocalRandom.Next(maxValue); + int result = LocalRandom.Next(maxValue); + AssertInRange(result, 0, maxValue); + return result; } public override int Next(int minValue, int maxValue) @@ -208,10 +265,17 @@ public override int Next(int minValue, int maxValue) ThrowMinMaxValueSwapped(); } - return LocalRandom.Next(minValue, maxValue); + int result = LocalRandom.Next(minValue, maxValue); + AssertInRange(result, minValue, maxValue); + return result; } - public override long NextInt64() => LocalRandom.NextInt64(); + public override long NextInt64() + { + long result = LocalRandom.NextInt64(); + AssertInRange(result, 0, long.MaxValue); + return result; + } public override long NextInt64(long maxValue) { @@ -220,7 +284,9 @@ public override long NextInt64(long maxValue) ThrowMaxValueMustBeNonNegative(); } - return LocalRandom.NextInt64(maxValue); + long result = LocalRandom.NextInt64(maxValue); + AssertInRange(result, 0, maxValue); + return result; } public override long NextInt64(long minValue, long maxValue) @@ -230,12 +296,24 @@ public override long NextInt64(long minValue, long maxValue) ThrowMinMaxValueSwapped(); } - return LocalRandom.NextInt64(minValue, maxValue); + long result = LocalRandom.NextInt64(minValue, maxValue); + AssertInRange(result, minValue, maxValue); + return result; } - public override float NextSingle() => LocalRandom.NextSingle(); + public override float NextSingle() + { + float result = LocalRandom.NextSingle(); + AssertInRange(result); + return result; + } - public override double NextDouble() => LocalRandom.NextDouble(); + public override double NextDouble() + { + double result = LocalRandom.NextDouble(); + AssertInRange(result); + return result; + } public override void NextBytes(byte[] buffer) { diff --git a/src/libraries/System.Private.CoreLib/src/System/ReadOnlySpan.cs b/src/libraries/System.Private.CoreLib/src/System/ReadOnlySpan.cs index bd2a26583d731f..0ba9ecfce1d045 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ReadOnlySpan.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ReadOnlySpan.cs @@ -274,7 +274,7 @@ public void CopyTo(Span destination) if ((uint)_length <= (uint)destination.Length) { - Buffer.Memmove(ref destination._pointer.Value, ref _pointer.Value, (nuint)_length); + Buffer.Memmove(ref destination._pointer.Value, ref _pointer.Value, (uint)_length); } else { @@ -295,7 +295,7 @@ public bool TryCopyTo(Span destination) bool retVal = false; if ((uint)_length <= (uint)destination.Length) { - Buffer.Memmove(ref destination._pointer.Value, ref _pointer.Value, (nuint)_length); + Buffer.Memmove(ref destination._pointer.Value, ref _pointer.Value, (uint)_length); retVal = true; } return retVal; diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Metadata/MetadataUpdateHandlerAttribute.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Metadata/MetadataUpdateHandlerAttribute.cs new file mode 100644 index 00000000000000..7af3be1644d8ac --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Metadata/MetadataUpdateHandlerAttribute.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; + +namespace System.Reflection.Metadata +{ + /// Specifies a type that should receive notifications of metadata updates. + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class MetadataUpdateHandlerAttribute : Attribute + { + /// Initializes the attribute. + /// A type that handles metadata updates and that should be notified when any occur. + public MetadataUpdateHandlerAttribute([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type handlerType) => + HandlerType = handlerType; + + /// Gets the type that handles metadata updates and that should be notified when any occur. + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + public Type HandlerType { get; } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.NoCom.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.NoCom.cs index 3fa9d56f5fd560..450b660185096c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.NoCom.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.NoCom.cs @@ -15,12 +15,6 @@ public static int GetHRForException(Exception? e) return e?.HResult ?? 0; } - [SupportedOSPlatform("windows")] - public static int AddRef(IntPtr pUnk) - { - throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop); - } - public static bool AreComObjectsAvailableForCleanup() => false; [SupportedOSPlatform("windows")] @@ -214,18 +208,6 @@ public static bool IsTypeVisibleFromCom(Type t) return false; } - [SupportedOSPlatform("windows")] - public static int QueryInterface(IntPtr pUnk, ref Guid iid, out IntPtr ppv) - { - throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop); - } - - [SupportedOSPlatform("windows")] - public static int Release(IntPtr pUnk) - { - throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop); - } - [SupportedOSPlatform("windows")] public static int ReleaseComObject(object o) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs index 81c87972f78429..542f3ba41b19e2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs @@ -144,6 +144,34 @@ public static int SizeOf(Type t) public static int SizeOf() => SizeOf(typeof(T)); + public static unsafe int QueryInterface(IntPtr pUnk, ref Guid iid, out IntPtr ppv) + { + if (pUnk == IntPtr.Zero) + throw new ArgumentNullException(nameof(pUnk)); + + fixed (Guid* pIID = &iid) + fixed (IntPtr* p = &ppv) + { + return ((delegate* unmanaged)(*(*(void***)pUnk + 0 /* IUnknown.QueryInterface slot */)))(pUnk, pIID, p); + } + } + + public static unsafe int AddRef(IntPtr pUnk) + { + if (pUnk == IntPtr.Zero) + throw new ArgumentNullException(nameof(pUnk)); + + return ((delegate* unmanaged)(*(*(void***)pUnk + 1 /* IUnknown.AddRef slot */)))(pUnk); + } + + public static unsafe int Release(IntPtr pUnk) + { + if (pUnk == IntPtr.Zero) + throw new ArgumentNullException(nameof(pUnk)); + + return ((delegate* unmanaged)(*(*(void***)pUnk + 2 /* IUnknown.Release slot */)))(pUnk); + } + /// /// IMPORTANT NOTICE: This method does not do any verification on the array. /// It must be used with EXTREME CAUTION since passing in invalid index or @@ -932,7 +960,7 @@ public static unsafe IntPtr StringToHGlobalUni(string? s) IntPtr ptr = AllocHGlobal((IntPtr)nb); - s.AsSpan().CopyTo(new Span((char*)ptr, s.Length)); + s.CopyTo(new Span((char*)ptr, s.Length)); ((char*)ptr)[s.Length] = '\0'; return ptr; @@ -979,7 +1007,7 @@ public static unsafe IntPtr StringToCoTaskMemUni(string? s) IntPtr ptr = AllocCoTaskMem(nb); - s.AsSpan().CopyTo(new Span((char*)ptr, s.Length)); + s.CopyTo(new Span((char*)ptr, s.Length)); ((char*)ptr)[s.Length] = '\0'; return ptr; @@ -1205,7 +1233,7 @@ public static unsafe IntPtr StringToBSTR(string? s) IntPtr bstr = AllocBSTR(s.Length); - s.AsSpan().CopyTo(new Span((char*)bstr, s.Length)); // AllocBSTR already included the null terminator + s.CopyTo(new Span((char*)bstr, s.Length)); // AllocBSTR already included the null terminator return bstr; } diff --git a/src/libraries/System.Private.CoreLib/src/System/SR.cs b/src/libraries/System.Private.CoreLib/src/System/SR.cs index c7db4dcdf29604..097aeffb0d8712 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SR.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SR.cs @@ -17,12 +17,6 @@ internal static partial class SR private static int _infinitelyRecursingCount; private static bool _resourceManagerInited; - // Needed for debugger integration - internal static string GetResourceString(string resourceKey) - { - return GetResourceString(resourceKey, null); - } - private static string InternalGetResourceString(string key) { if (key.Length == 0) diff --git a/src/libraries/System.Private.CoreLib/src/System/Span.cs b/src/libraries/System.Private.CoreLib/src/System/Span.cs index f48bdf91f23c4c..60de1c03286654 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Span.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Span.cs @@ -348,7 +348,7 @@ public void CopyTo(Span destination) if ((uint)_length <= (uint)destination.Length) { - Buffer.Memmove(ref destination._pointer.Value, ref _pointer.Value, (nuint)_length); + Buffer.Memmove(ref destination._pointer.Value, ref _pointer.Value, (uint)_length); } else { @@ -369,7 +369,7 @@ public bool TryCopyTo(Span destination) bool retVal = false; if ((uint)_length <= (uint)destination.Length) { - Buffer.Memmove(ref destination._pointer.Value, ref _pointer.Value, (nuint)_length); + Buffer.Memmove(ref destination._pointer.Value, ref _pointer.Value, (uint)_length); retVal = true; } return retVal; diff --git a/src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs b/src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs index 8c6a7f1420d439..558d0a399af12b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs +++ b/src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs @@ -1141,7 +1141,7 @@ private string ReplaceHelper(int oldValueLength, string newValue, ReadOnlySpanCopies the contents of this string into the destination span. + /// The span into which to copy this string's contents. + /// The destination span is shorter than the source string. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void CopyTo(Span destination) + { + if ((uint)Length <= (uint)destination.Length) + { + Buffer.Memmove(ref destination._pointer.Value, ref _firstChar, (uint)Length); + } + else + { + ThrowHelper.ThrowArgumentException_DestinationTooShort(); + } + } + + /// Copies the contents of this string into the destination span. + /// The span into which to copy this string's contents. + /// true if the data was copied; false if the destination was too short to fit the contents of the string. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal bool TryCopyTo(Span destination) + { + bool retVal = false; + if ((uint)Length <= (uint)destination.Length) + { + Buffer.Memmove(ref destination._pointer.Value, ref _firstChar, (uint)Length); + retVal = true; + } + return retVal; + } + // Returns the entire string as an array of characters. public char[] ToCharArray() { diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/TranscodingStream.cs b/src/libraries/System.Private.CoreLib/src/System/Text/TranscodingStream.cs index 5ad89cd3f98a65..6a459c5f774fac 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/TranscodingStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/TranscodingStream.cs @@ -312,18 +312,28 @@ public override int Read(Span buffer) try { - // Beware: Use our constant value instead of 'rentedBytes.Length' for the count - // parameter below. The reason for this is that the array pool could've returned - // a larger-than-expected array, but our worst-case expansion calculations - // performed earlier didn't take that into account. + int pendingReadDataPopulatedJustNow; + bool isEofReached; - int innerBytesReadJustNow = _innerStream.Read(rentedBytes, 0, DefaultReadByteBufferSize); - bool isEofReached = (innerBytesReadJustNow == 0); + do + { + // Beware: Use our constant value instead of 'rentedBytes.Length' for the count + // parameter below. The reason for this is that the array pool could've returned + // a larger-than-expected array, but our worst-case expansion calculations + // performed earlier didn't take that into account. - // convert bytes [inner] -> chars, then convert chars -> bytes [this] + int innerBytesReadJustNow = _innerStream.Read(rentedBytes, 0, DefaultReadByteBufferSize); + isEofReached = (innerBytesReadJustNow == 0); - int charsDecodedJustNow = _innerDecoder.GetChars(rentedBytes, 0, innerBytesReadJustNow, rentedChars, 0, flush: isEofReached); - int pendingReadDataPopulatedJustNow = _thisEncoder.GetBytes(rentedChars, 0, charsDecodedJustNow, _readBuffer, 0, flush: isEofReached); + // Convert bytes [inner] -> chars, then convert chars -> bytes [this]. + // We can't return 0 to our caller until inner stream EOF has been reached. But if the + // inner stream returns a non-empty but incomplete buffer, GetBytes may return 0 anyway + // since it can't yet make forward progress on the input data. If this happens, we'll + // loop so that we don't return 0 to our caller until we truly see inner stream EOF. + + int charsDecodedJustNow = _innerDecoder.GetChars(rentedBytes, 0, innerBytesReadJustNow, rentedChars, 0, flush: isEofReached); + pendingReadDataPopulatedJustNow = _thisEncoder.GetBytes(rentedChars, 0, charsDecodedJustNow, _readBuffer, 0, flush: isEofReached); + } while (!isEofReached && pendingReadDataPopulatedJustNow == 0); _readBufferOffset = 0; _readBufferCount = pendingReadDataPopulatedJustNow; @@ -381,18 +391,28 @@ async ValueTask ReadAsyncCore(Memory buffer, CancellationToken cancel try { - // Beware: Use our constant value instead of 'rentedBytes.Length' when creating - // the Mem struct. The reason for this is that the array pool could've returned - // a larger-than-expected array, but our worst-case expansion calculations - // performed earlier didn't take that into account. - - int innerBytesReadJustNow = await _innerStream.ReadAsync(rentedBytes.AsMemory(0, DefaultReadByteBufferSize), cancellationToken).ConfigureAwait(false); - bool isEofReached = (innerBytesReadJustNow == 0); - - // convert bytes [inner] -> chars, then convert chars -> bytes [this] + int pendingReadDataPopulatedJustNow; + bool isEofReached; - int charsDecodedJustNow = _innerDecoder.GetChars(rentedBytes, 0, innerBytesReadJustNow, rentedChars, 0, flush: isEofReached); - int pendingReadDataPopulatedJustNow = _thisEncoder.GetBytes(rentedChars, 0, charsDecodedJustNow, _readBuffer, 0, flush: isEofReached); + do + { + // Beware: Use our constant value instead of 'rentedBytes.Length' when creating + // the Mem struct. The reason for this is that the array pool could've returned + // a larger-than-expected array, but our worst-case expansion calculations + // performed earlier didn't take that into account. + + int innerBytesReadJustNow = await _innerStream.ReadAsync(rentedBytes.AsMemory(0, DefaultReadByteBufferSize), cancellationToken).ConfigureAwait(false); + isEofReached = (innerBytesReadJustNow == 0); + + // Convert bytes [inner] -> chars, then convert chars -> bytes [this]. + // We can't return 0 to our caller until inner stream EOF has been reached. But if the + // inner stream returns a non-empty but incomplete buffer, GetBytes may return 0 anyway + // since it can't yet make forward progress on the input data. If this happens, we'll + // loop so that we don't return 0 to our caller until we truly see inner stream EOF. + + int charsDecodedJustNow = _innerDecoder.GetChars(rentedBytes, 0, innerBytesReadJustNow, rentedChars, 0, flush: isEofReached); + pendingReadDataPopulatedJustNow = _thisEncoder.GetBytes(rentedChars, 0, charsDecodedJustNow, _readBuffer, 0, flush: isEofReached); + } while (!isEofReached && pendingReadDataPopulatedJustNow == 0); _readBufferOffset = 0; _readBufferCount = pendingReadDataPopulatedJustNow; diff --git a/src/libraries/System.Private.CoreLib/src/System/Type.Enum.cs b/src/libraries/System.Private.CoreLib/src/System/Type.Enum.cs index a025edbb312f7d..8915fc43b037a4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Type.Enum.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Type.Enum.cs @@ -124,7 +124,7 @@ private void GetEnumData(out string[] enumNames, out Array enumValues) // Insertion Sort these values in ascending order. // We use this O(n^2) algorithm, but it turns out that most of the time the elements are already in sorted order and // the common case performance will be faster than quick sorting this. - IComparer comparer = Comparer.Default; + Comparer comparer = Comparer.Default; for (int i = 1; i < values.Length; i++) { int j = i; diff --git a/src/libraries/System.Private.DataContractSerialization/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Private.DataContractSerialization/src/ILLink/ILLink.Suppressions.xml index 032094abce4ffd..7e36a2f01ffabd 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Private.DataContractSerialization/src/ILLink/ILLink.Suppressions.xml @@ -21,87 +21,81 @@ ILLink - IL2055 + IL2046 member - M:System.Runtime.Serialization.ClassDataContract.ClassDataContractCriticalHelper.SetKeyValuePairAdapterFlags(System.Type) + M:System.Runtime.Serialization.Json.DataContractJsonSerializerImpl.InternalReadObject(System.Runtime.Serialization.XmlReaderDelegator,System.Boolean) ILLink - IL2055 + IL2046 member - M:System.Runtime.Serialization.CollectionDataContract.CollectionDataContractCriticalHelper.GetCollectionElementType + M:System.Runtime.Serialization.Json.DataContractJsonSerializerImpl.InternalWriteObject(System.Runtime.Serialization.XmlWriterDelegator,System.Object) ILLink - IL2055 + IL2046 member - M:System.Runtime.Serialization.CollectionDataContract.CollectionDataContractCriticalHelper.IncrementCollectionCount(System.Runtime.Serialization.XmlWriterDelegator,System.Object,System.Runtime.Serialization.XmlObjectSerializerWriteContext) + M:System.Runtime.Serialization.Json.DataContractJsonSerializerImpl.InternalWriteObjectContent(System.Runtime.Serialization.XmlWriterDelegator,System.Object) ILLink - IL2055 + IL2046 member - M:System.Runtime.Serialization.CollectionDataContract.IsCollectionOrTryCreate(System.Type,System.Boolean,System.Runtime.Serialization.DataContract@,System.Type@,System.Boolean) + M:System.Runtime.Serialization.Json.ReflectionJsonReader.ReflectionReadDictionaryItem(System.Runtime.Serialization.XmlReaderDelegator,System.Runtime.Serialization.XmlObjectSerializerReadContext,System.Runtime.Serialization.CollectionDataContract) ILLink - IL2055 + IL2046 member - M:System.Runtime.Serialization.DataContract.DataContractCriticalHelper.GetDataContractAdapterType(System.Type) + M:System.Runtime.Serialization.Json.XmlObjectSerializerReadContextComplexJson.ReadDataContractValue(System.Runtime.Serialization.DataContract,System.Runtime.Serialization.XmlReaderDelegator) ILLink - IL2055 + IL2046 member - M:System.Runtime.Serialization.DataContract.ImportKnownTypeAttributes(System.Type,System.Collections.Generic.Dictionary{System.Type,System.Type},System.Collections.Generic.Dictionary{System.Xml.XmlQualifiedName,System.Runtime.Serialization.DataContract}@) + M:System.Runtime.Serialization.Json.XmlObjectSerializerWriteContextComplexJson.WriteDataContractValue(System.Runtime.Serialization.DataContract,System.Runtime.Serialization.XmlWriterDelegator,System.Object,System.RuntimeTypeHandle) ILLink IL2055 member - M:System.Runtime.Serialization.Globals.get_TypeOfHashtable - - - ILLink - IL2055 - member - M:System.Runtime.Serialization.Json.DataContractJsonSerializerImpl.AddCollectionItemTypeToKnownTypes(System.Type) + M:System.Runtime.Serialization.ClassDataContract.ClassDataContractCriticalHelper.SetKeyValuePairAdapterFlags(System.Type) ILLink IL2055 member - M:System.Runtime.Serialization.Json.JsonDataContract.JsonDataContractCriticalHelper.AddCollectionItemContractsToKnownDataContracts + M:System.Runtime.Serialization.CollectionDataContract.CollectionDataContractCriticalHelper.GetCollectionElementType ILLink IL2055 member - M:System.Runtime.Serialization.Json.JsonFormatReaderGenerator.CriticalHelper.GenerateClassReader(System.Runtime.Serialization.ClassDataContract) + M:System.Runtime.Serialization.CollectionDataContract.CollectionDataContractCriticalHelper.IncrementCollectionCount(System.Runtime.Serialization.XmlWriterDelegator,System.Object,System.Runtime.Serialization.XmlObjectSerializerWriteContext) ILLink IL2055 member - M:System.Runtime.Serialization.Json.JsonFormatReaderGenerator.CriticalHelper.ReadCollection(System.Runtime.Serialization.CollectionDataContract) + M:System.Runtime.Serialization.CollectionDataContract.IsCollectionOrTryCreate(System.Type,System.Boolean,System.Runtime.Serialization.DataContract@,System.Type@,System.Boolean) ILLink IL2055 member - M:System.Runtime.Serialization.Json.JsonFormatReaderGenerator.CriticalHelper.WrapNullableObject(System.Reflection.Emit.LocalBuilder,System.Reflection.Emit.LocalBuilder,System.Int32) + M:System.Runtime.Serialization.DataContract.DataContractCriticalHelper.GetDataContractAdapterType(System.Type) ILLink IL2055 member - M:System.Runtime.Serialization.Json.JsonFormatWriterGenerator.CriticalHelper.InitArgs(System.Type) + M:System.Runtime.Serialization.DataContract.ImportKnownTypeAttributes(System.Type,System.Collections.Generic.Dictionary{System.Type,System.Type},System.Collections.Generic.Dictionary{System.Xml.XmlQualifiedName,System.Runtime.Serialization.DataContract}@) ILLink IL2055 member - M:System.Runtime.Serialization.Json.JsonFormatWriterGenerator.CriticalHelper.WriteCollection(System.Runtime.Serialization.CollectionDataContract) + M:System.Runtime.Serialization.Globals.get_TypeOfHashtable ILLink @@ -169,18 +163,6 @@ member M:System.Runtime.Serialization.FastInvokerBuilder.GetMakeNewInstanceFunc(System.Type) - - ILLink - IL2060 - member - M:System.Runtime.Serialization.Json.JsonFormatReaderGenerator.CriticalHelper.ReadCollection(System.Runtime.Serialization.CollectionDataContract) - - - ILLink - IL2060 - member - M:System.Runtime.Serialization.Json.JsonFormatWriterGenerator.CriticalHelper.WriteCollection(System.Runtime.Serialization.CollectionDataContract) - ILLink IL2060 @@ -229,12 +211,6 @@ member M:System.Runtime.Serialization.XmlFormatReaderGenerator.UnsafeGetUninitializedObject(System.Type) - - ILLink - IL2067 - member - M:System.Runtime.Serialization.SurrogateDataContract.GetUninitializedObject(System.Type) - ILLink IL2070 @@ -271,18 +247,6 @@ member M:System.Runtime.Serialization.DataContract.ImportKnownTypeAttributes(System.Type,System.Collections.Generic.Dictionary{System.Type,System.Type},System.Collections.Generic.Dictionary{System.Xml.XmlQualifiedName,System.Runtime.Serialization.DataContract}@) - - ILLink - IL2070 - member - M:System.Runtime.Serialization.Json.JsonFormatReaderGenerator.CriticalHelper.BeginMethod(System.Runtime.Serialization.CodeGenerator,System.String,System.Type,System.Boolean) - - - ILLink - IL2070 - member - M:System.Runtime.Serialization.Json.JsonFormatWriterGenerator.CriticalHelper.BeginMethod(System.Runtime.Serialization.CodeGenerator,System.String,System.Type,System.Boolean) - ILLink IL2070 @@ -361,36 +325,6 @@ member M:System.Runtime.Serialization.EnumDataContract.EnumDataContractCriticalHelper.ImportDataMembers - - ILLink - IL2075 - member - M:System.Runtime.Serialization.Json.JsonFormatReaderGenerator.CriticalHelper.CreateObject(System.Runtime.Serialization.ClassDataContract) - - - ILLink - IL2075 - member - M:System.Runtime.Serialization.Json.JsonFormatReaderGenerator.CriticalHelper.ReadCollection(System.Runtime.Serialization.CollectionDataContract) - - - ILLink - IL2075 - member - M:System.Runtime.Serialization.Json.JsonFormatReaderGenerator.CriticalHelper.ReadISerializable(System.Runtime.Serialization.ClassDataContract) - - - ILLink - IL2075 - member - M:System.Runtime.Serialization.Json.JsonFormatReaderGenerator.CriticalHelper.WrapNullableObject(System.Reflection.Emit.LocalBuilder,System.Reflection.Emit.LocalBuilder,System.Int32) - - - ILLink - IL2075 - member - M:System.Runtime.Serialization.Json.JsonFormatWriterGenerator.CriticalHelper.WriteCollection(System.Runtime.Serialization.CollectionDataContract) - ILLink IL2075 diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CodeGenerator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CodeGenerator.cs index 780866c355f498..87625a843be20f 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CodeGenerator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CodeGenerator.cs @@ -12,6 +12,7 @@ using System.Security; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization.Json; namespace System.Runtime.Serialization { @@ -131,7 +132,7 @@ internal void BeginMethod(DynamicMethod dynamicMethod, Type delegateType, string internal void BeginMethod(string methodName, Type delegateType, bool allowPrivateMemberAccess) { - MethodInfo signature = delegateType.GetMethod("Invoke")!; + MethodInfo signature = JsonFormatWriterGenerator.GetInvokeMethod(delegateType); ParameterInfo[] parameters = signature.GetParameters(); Type[] paramTypes = new Type[parameters.Length]; for (int i = 0; i < parameters.Length; i++) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/DataContractJsonSerializer.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/DataContractJsonSerializer.cs index 4a66fdf2b83c1d..f29eafb3ddb208 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/DataContractJsonSerializer.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/DataContractJsonSerializer.cs @@ -26,6 +26,8 @@ public sealed class DataContractJsonSerializer : XmlObjectSerializer private const char LOW_SURROGATE_END = (char)0xdfff; private const char MAX_CHAR = (char)0xfffe; private const char WHITESPACE = ' '; + internal const string SerializerTrimmerWarning = "Json Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the " + + "required types are preserved."; internal IList? knownTypeList; @@ -40,36 +42,43 @@ public sealed class DataContractJsonSerializer : XmlObjectSerializer private readonly DataContractJsonSerializerImpl _serializer; private readonly bool _ignoreExtensionDataObject; + [RequiresUnreferencedCode(SerializerTrimmerWarning)] public DataContractJsonSerializer(Type type) { _serializer = new DataContractJsonSerializerImpl(type); } + [RequiresUnreferencedCode(SerializerTrimmerWarning)] public DataContractJsonSerializer(Type type, string? rootName) : this(type, rootName, null) { } + [RequiresUnreferencedCode(SerializerTrimmerWarning)] public DataContractJsonSerializer(Type type, XmlDictionaryString? rootName) : this(type, rootName, null) { } + [RequiresUnreferencedCode(SerializerTrimmerWarning)] public DataContractJsonSerializer(Type type, IEnumerable? knownTypes) { _serializer = new DataContractJsonSerializerImpl(type, knownTypes); } + [RequiresUnreferencedCode(SerializerTrimmerWarning)] public DataContractJsonSerializer(Type type, string? rootName, IEnumerable? knownTypes) : this(type, new DataContractJsonSerializerSettings() { RootName = rootName, KnownTypes = knownTypes }) { } + [RequiresUnreferencedCode(SerializerTrimmerWarning)] public DataContractJsonSerializer(Type type, XmlDictionaryString? rootName, IEnumerable? knownTypes) { _serializer = new DataContractJsonSerializerImpl(type, rootName, knownTypes); } + [RequiresUnreferencedCode(SerializerTrimmerWarning)] public DataContractJsonSerializer(Type type, DataContractJsonSerializerSettings? settings) { _serializer = new DataContractJsonSerializerImpl(type, settings); @@ -425,11 +434,13 @@ internal static string ConvertXmlNameToJsonName(string xmlName) return (xmlName == null) ? null : new XmlDictionary().Add(ConvertXmlNameToJsonName(xmlName.Value)); } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] internal static object? ReadJsonValue(DataContract contract, XmlReaderDelegator reader, XmlObjectSerializerReadContextComplexJson context) { return JsonDataContract.GetJsonDataContract(contract).ReadJsonValue(reader, context); } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] internal static void WriteJsonValue(JsonDataContract contract, XmlWriterDelegator writer, object graph, XmlObjectSerializerWriteContextComplexJson context, RuntimeTypeHandle declaredTypeHandle) { contract.WriteJsonValue(writer, graph, context, declaredTypeHandle); @@ -497,21 +508,25 @@ internal sealed class DataContractJsonSerializerImpl : XmlObjectSerializer private DateTimeFormat? _dateTimeFormat; private bool _useSimpleDictionaryFormat; + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public DataContractJsonSerializerImpl(Type type) : this(type, (IEnumerable?)null) { } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public DataContractJsonSerializerImpl(Type type, IEnumerable? knownTypes) : this(type, null, knownTypes, int.MaxValue, false, false) { } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public DataContractJsonSerializerImpl(Type type, XmlDictionaryString? rootName, IEnumerable? knownTypes) : this(type, rootName, knownTypes, int.MaxValue, false, false) { } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] internal DataContractJsonSerializerImpl(Type type, XmlDictionaryString? rootName, IEnumerable? knownTypes, @@ -523,6 +538,7 @@ internal DataContractJsonSerializerImpl(Type type, Initialize(type, rootName, knownTypes, maxItemsInObjectGraph, ignoreExtensionDataObject, emitTypeInformation, false, null, false); } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public DataContractJsonSerializerImpl(Type type, DataContractJsonSerializerSettings? settings) { if (settings == null) @@ -783,6 +799,7 @@ internal static bool IsJsonLocalName(XmlReaderDelegator reader, string elementNa return false; } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] internal static object? ReadJsonValue(DataContract contract, XmlReaderDelegator reader, XmlObjectSerializerReadContextComplexJson? context) { return JsonDataContract.GetJsonDataContract(contract).ReadJsonValue(reader, context); @@ -793,6 +810,7 @@ internal static void WriteJsonNull(XmlWriterDelegator writer) writer.WriteAttributeString(null, JsonGlobals.typeString, null, JsonGlobals.nullString); // prefix // namespace } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] internal static void WriteJsonValue(JsonDataContract contract, XmlWriterDelegator writer, object graph, XmlObjectSerializerWriteContextComplexJson? context, RuntimeTypeHandle declaredTypeHandle) { contract.WriteJsonValue(writer, graph, context, declaredTypeHandle); @@ -818,6 +836,7 @@ internal override bool InternalIsStartObject(XmlReaderDelegator reader) return IsJsonLocalName(reader, RootName.Value); } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] internal override object? InternalReadObject(XmlReaderDelegator xmlReader, bool verifyObjectName) { if (MaxItemsInObjectGraph == 0) @@ -852,6 +871,7 @@ internal override void InternalWriteEndObject(XmlWriterDelegator writer) writer.WriteEndElement(); } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] internal override void InternalWriteObject(XmlWriterDelegator writer, object? graph) { InternalWriteStartObject(writer, graph); @@ -859,6 +879,7 @@ internal override void InternalWriteObject(XmlWriterDelegator writer, object? gr InternalWriteEndObject(writer); } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] internal override void InternalWriteObjectContent(XmlWriterDelegator writer, object? graph) { if (MaxItemsInObjectGraph == 0) @@ -924,6 +945,7 @@ internal override void InternalWriteStartObject(XmlWriterDelegator writer, objec } } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] private void AddCollectionItemTypeToKnownTypes(Type knownType) { Type? itemType; @@ -940,6 +962,7 @@ private void AddCollectionItemTypeToKnownTypes(Type knownType) } [MemberNotNull(nameof(_rootType))] + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] private void Initialize(Type type, IEnumerable? knownTypes, int maxItemsInObjectGraph, @@ -978,6 +1001,7 @@ private void Initialize(Type type, } [MemberNotNull(nameof(_rootType))] + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] private void Initialize(Type type, XmlDictionaryString? rootName, IEnumerable? knownTypes, diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonByteArrayDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonByteArrayDataContract.cs index b0ec7cdd07a093..eb43e5e96253c0 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonByteArrayDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonByteArrayDataContract.cs @@ -6,16 +6,19 @@ using System.Text; using System.Diagnostics; using System.Xml; +using System.Diagnostics.CodeAnalysis; namespace System.Runtime.Serialization.Json { internal sealed class JsonByteArrayDataContract : JsonDataContract { + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public JsonByteArrayDataContract(ByteArrayDataContract traditionalByteArrayDataContract) : base(traditionalByteArrayDataContract) { } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public override object? ReadJsonValueCore(XmlReaderDelegator jsonReader, XmlObjectSerializerReadContextComplexJson? context) { if (context == null) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonClassDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonClassDataContract.cs index 4c4c0d33b6e327..8b3494225cb083 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonClassDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonClassDataContract.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.Collections.Generic; using System.Runtime.CompilerServices; +using System.Diagnostics.CodeAnalysis; namespace System.Runtime.Serialization.Json { @@ -13,6 +14,7 @@ internal sealed class JsonClassDataContract : JsonDataContract { private readonly JsonClassDataContractCriticalHelper _helper; + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public JsonClassDataContract(ClassDataContract traditionalDataContract) : base(new JsonClassDataContractCriticalHelper(traditionalDataContract)) { @@ -26,6 +28,7 @@ private JsonFormatClassReaderDelegate CreateJsonFormatReaderDelegate() internal JsonFormatClassReaderDelegate JsonFormatReaderDelegate { + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] get { if (_helper.JsonFormatReaderDelegate == null) @@ -60,6 +63,7 @@ private JsonFormatClassWriterDelegate CreateJsonFormatWriterDelegate() internal JsonFormatClassWriterDelegate JsonFormatWriterDelegate { + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] get { if (_helper.JsonFormatWriterDelegate == null) @@ -93,6 +97,7 @@ internal JsonFormatClassWriterDelegate JsonFormatWriterDelegate private ClassDataContract TraditionalClassDataContract => _helper.TraditionalClassDataContract; + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public override object? ReadJsonValueCore(XmlReaderDelegator jsonReader, XmlObjectSerializerReadContextComplexJson? context) { jsonReader.Read(); @@ -101,6 +106,7 @@ internal JsonFormatClassWriterDelegate JsonFormatWriterDelegate return o; } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public override void WriteJsonValueCore(XmlWriterDelegator jsonWriter, object obj, XmlObjectSerializerWriteContextComplexJson? context, RuntimeTypeHandle declaredTypeHandle) { Debug.Assert(context != null); @@ -116,6 +122,7 @@ private sealed class JsonClassDataContractCriticalHelper : JsonDataContractCriti private readonly ClassDataContract _traditionalClassDataContract; private readonly string _typeName; + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public JsonClassDataContractCriticalHelper(ClassDataContract traditionalDataContract) : base(traditionalDataContract) { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonCollectionDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonCollectionDataContract.cs index 13693553deb9b6..175835807faa24 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonCollectionDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonCollectionDataContract.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.CompilerServices; using System.Threading; @@ -13,6 +14,7 @@ internal sealed class JsonCollectionDataContract : JsonDataContract { private readonly JsonCollectionDataContractCriticalHelper _helper; + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public JsonCollectionDataContract(CollectionDataContract traditionalDataContract) : base(new JsonCollectionDataContractCriticalHelper(traditionalDataContract)) { @@ -26,6 +28,7 @@ private JsonFormatCollectionReaderDelegate CreateJsonFormatReaderDelegate() internal JsonFormatCollectionReaderDelegate JsonFormatReaderDelegate { + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] get { if (_helper.JsonFormatReaderDelegate == null) @@ -60,6 +63,7 @@ private JsonFormatGetOnlyCollectionReaderDelegate CreateJsonFormatGetOnlyReaderD internal JsonFormatGetOnlyCollectionReaderDelegate JsonFormatGetOnlyReaderDelegate { + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] get { if (_helper.JsonFormatGetOnlyReaderDelegate == null) @@ -93,6 +97,7 @@ internal JsonFormatGetOnlyCollectionReaderDelegate JsonFormatGetOnlyReaderDelega } } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] private JsonFormatCollectionWriterDelegate CreateJsonFormatWriterDelegate() { return new ReflectionJsonFormatWriter().ReflectionWriteCollection; @@ -101,6 +106,7 @@ private JsonFormatCollectionWriterDelegate CreateJsonFormatWriterDelegate() internal JsonFormatCollectionWriterDelegate JsonFormatWriterDelegate { + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] get { if (_helper.JsonFormatWriterDelegate == null) @@ -130,6 +136,7 @@ internal JsonFormatCollectionWriterDelegate JsonFormatWriterDelegate private CollectionDataContract TraditionalCollectionDataContract => _helper.TraditionalCollectionDataContract; + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public override object? ReadJsonValueCore(XmlReaderDelegator jsonReader, XmlObjectSerializerReadContextComplexJson? context) { Debug.Assert(context != null); @@ -150,6 +157,7 @@ internal JsonFormatCollectionWriterDelegate JsonFormatWriterDelegate return o; } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public override void WriteJsonValueCore(XmlWriterDelegator jsonWriter, object obj, XmlObjectSerializerWriteContextComplexJson? context, RuntimeTypeHandle declaredTypeHandle) { Debug.Assert(context != null); @@ -165,6 +173,7 @@ private sealed class JsonCollectionDataContractCriticalHelper : JsonDataContract private JsonFormatCollectionWriterDelegate? _jsonFormatWriterDelegate; private readonly CollectionDataContract _traditionalCollectionDataContract; + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public JsonCollectionDataContractCriticalHelper(CollectionDataContract traditionalDataContract) : base(traditionalDataContract) { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonDataContract.cs index 6482fd752639c1..a638ff086e3620 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonDataContract.cs @@ -7,6 +7,7 @@ using System.Reflection; using System.Xml; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; namespace System.Runtime.Serialization.Json { @@ -14,6 +15,7 @@ internal class JsonDataContract { private readonly JsonDataContractCriticalHelper _helper; + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] protected JsonDataContract(DataContract traditionalDataContract) { _helper = new JsonDataContractCriticalHelper(traditionalDataContract); @@ -60,11 +62,13 @@ internal static JsonReadWriteDelegates GetReadWriteDelegatesFromGeneratedAssembl return result; } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public static JsonDataContract GetJsonDataContract(DataContract traditionalDataContract) { return JsonDataContractCriticalHelper.GetJsonDataContract(traditionalDataContract); } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public object? ReadJsonValue(XmlReaderDelegator jsonReader, XmlObjectSerializerReadContextComplexJson? context) { PushKnownDataContracts(context); @@ -73,11 +77,13 @@ public static JsonDataContract GetJsonDataContract(DataContract traditionalDataC return deserializedObject; } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public virtual object? ReadJsonValueCore(XmlReaderDelegator jsonReader, XmlObjectSerializerReadContextComplexJson? context) { return TraditionalDataContract.ReadXmlValue(jsonReader, context); } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public void WriteJsonValue(XmlWriterDelegator jsonWriter, object obj, XmlObjectSerializerWriteContextComplexJson? context, RuntimeTypeHandle declaredTypeHandle) { PushKnownDataContracts(context); @@ -85,6 +91,7 @@ public void WriteJsonValue(XmlWriterDelegator jsonWriter, object obj, XmlObjectS PopKnownDataContracts(context); } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public virtual void WriteJsonValueCore(XmlWriterDelegator jsonWriter, object obj, XmlObjectSerializerWriteContextComplexJson? context, RuntimeTypeHandle declaredTypeHandle) { TraditionalDataContract.WriteXmlValue(jsonWriter, obj, context); @@ -141,6 +148,7 @@ internal class JsonDataContractCriticalHelper private readonly DataContract _traditionalDataContract; private readonly string _typeName; + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] internal JsonDataContractCriticalHelper(DataContract traditionalDataContract) { _traditionalDataContract = traditionalDataContract; @@ -154,6 +162,7 @@ internal JsonDataContractCriticalHelper(DataContract traditionalDataContract) internal virtual string TypeName => _typeName; + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public static JsonDataContract GetJsonDataContract(DataContract traditionalDataContract) { int id = JsonDataContractCriticalHelper.GetId(traditionalDataContract.UnderlyingType.TypeHandle); @@ -199,6 +208,7 @@ internal static int GetId(RuntimeTypeHandle typeHandle) } } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] private static JsonDataContract CreateJsonDataContract(int id, DataContract traditionalDataContract) { lock (s_createDataContractLock) @@ -262,6 +272,7 @@ private static JsonDataContract CreateJsonDataContract(int id, DataContract trad } } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] private void AddCollectionItemContractsToKnownDataContracts() { if (_traditionalDataContract.KnownDataContracts != null) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonEnumDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonEnumDataContract.cs index f27c5361ebd90a..7fe0a429c05fe2 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonEnumDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonEnumDataContract.cs @@ -1,12 +1,15 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; + namespace System.Runtime.Serialization.Json { internal sealed class JsonEnumDataContract : JsonDataContract { private readonly JsonEnumDataContractCriticalHelper _helper; + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public JsonEnumDataContract(EnumDataContract traditionalDataContract) : base(new JsonEnumDataContractCriticalHelper(traditionalDataContract)) { @@ -15,6 +18,7 @@ public JsonEnumDataContract(EnumDataContract traditionalDataContract) public bool IsULong => _helper.IsULong; + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public override object? ReadJsonValueCore(XmlReaderDelegator jsonReader, XmlObjectSerializerReadContextComplexJson? context) { object enumValue; @@ -34,6 +38,7 @@ public JsonEnumDataContract(EnumDataContract traditionalDataContract) return enumValue; } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public override void WriteJsonValueCore(XmlWriterDelegator jsonWriter, object obj, XmlObjectSerializerWriteContextComplexJson? context, RuntimeTypeHandle declaredTypeHandle) { if (IsULong) @@ -50,6 +55,7 @@ private sealed class JsonEnumDataContractCriticalHelper : JsonDataContractCritic { private readonly bool _isULong; + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public JsonEnumDataContractCriticalHelper(EnumDataContract traditionalEnumDataContract) : base(traditionalEnumDataContract) { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatGeneratorStatics.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatGeneratorStatics.cs index ea6c9f016b29ba..43218be2133044 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatGeneratorStatics.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatGeneratorStatics.cs @@ -7,6 +7,7 @@ using System.Runtime.Serialization.Json; using System.Xml; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; namespace System.Runtime.Serialization { @@ -127,6 +128,7 @@ public static MethodInfo GetItemContractMethod } public static MethodInfo GetJsonDataContractMethod { + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] get { if (s_getJsonDataContractMethod == null) @@ -270,8 +272,10 @@ public static MethodInfo OnDeserializationMethod return s_onDeserializationMethod; } } + public static MethodInfo ReadJsonValueMethod { + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] get { if (s_readJsonValueMethod == null) @@ -415,6 +419,7 @@ public static MethodInfo WriteJsonNameWithMappingMethod } public static MethodInfo WriteJsonValueMethod { + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] get { if (s_writeJsonValueMethod == null) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatReaderGenerator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatReaderGenerator.cs index 49f3055ec24271..e4aeed0d50556b 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatReaderGenerator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatReaderGenerator.cs @@ -9,6 +9,7 @@ namespace System.Runtime.Serialization.Json using System; using System.Collections.Generic; using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Reflection.Emit; using System.Runtime; @@ -28,16 +29,19 @@ public JsonFormatReaderGenerator() _helper = new CriticalHelper(); } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public JsonFormatClassReaderDelegate GenerateClassReader(ClassDataContract classContract) { return _helper.GenerateClassReader(classContract); } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public JsonFormatCollectionReaderDelegate GenerateCollectionReader(CollectionDataContract collectionContract) { return _helper.GenerateCollectionReader(collectionContract); } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public JsonFormatGetOnlyCollectionReaderDelegate GenerateGetOnlyCollectionReader(CollectionDataContract collectionContract) { return _helper.GenerateGetOnlyCollectionReader(collectionContract); @@ -54,6 +58,7 @@ private sealed class CriticalHelper private ArgBuilder? _collectionContractArg; private ArgBuilder _emptyDictionaryStringArg = null!; // initialized in InitArgs + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public JsonFormatClassReaderDelegate GenerateClassReader(ClassDataContract classContract) { _ilg = new CodeGenerator(); @@ -115,6 +120,7 @@ public JsonFormatClassReaderDelegate GenerateClassReader(ClassDataContract class return (JsonFormatClassReaderDelegate)_ilg.EndMethod(); } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public JsonFormatCollectionReaderDelegate GenerateCollectionReader(CollectionDataContract collectionContract) { _ilg = GenerateCollectionReaderHelper(collectionContract, false /*isGetOnlyCollection*/); @@ -124,6 +130,7 @@ public JsonFormatCollectionReaderDelegate GenerateCollectionReader(CollectionDat return (JsonFormatCollectionReaderDelegate)_ilg.EndMethod(); } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public JsonFormatGetOnlyCollectionReaderDelegate GenerateGetOnlyCollectionReader(CollectionDataContract collectionContract) { _ilg = GenerateCollectionReaderHelper(collectionContract, true /*isGetOnlyCollection*/); @@ -164,7 +171,7 @@ private CodeGenerator GenerateCollectionReaderHelper(CollectionDataContract coll private void BeginMethod(CodeGenerator ilg, string methodName, Type delegateType, bool allowPrivateMemberAccess) { - MethodInfo signature = delegateType.GetMethod("Invoke")!; + MethodInfo signature = JsonFormatWriterGenerator.GetInvokeMethod(delegateType); ParameterInfo[] parameters = signature.GetParameters(); Type[] paramTypes = new Type[parameters.Length]; for (int i = 0; i < parameters.Length; i++) @@ -182,6 +189,7 @@ private void InitArgs() _memberNamesArg = _ilg.GetArg(3); } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] private void CreateObject(ClassDataContract classContract) { _objectType = classContract.UnderlyingType; @@ -264,6 +272,7 @@ private bool InvokeFactoryMethod(ClassDataContract classContract) return false; } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] private void ReadClass(ClassDataContract classContract) { if (classContract.HasExtensionData) @@ -288,6 +297,7 @@ private void ReadClass(ClassDataContract classContract) } } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] private void ReadMembers(ClassDataContract classContract, LocalBuilder? extensionDataLocal) { int memberCount = classContract.MemberNames!.Length; @@ -335,6 +345,7 @@ private void ReadMembers(ClassDataContract classContract, LocalBuilder? extensio _ilg.MarkLabel(endOfTypeLabel); } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] private int ReadMembers(ClassDataContract classContract, BitFlagsGenerator expectedElements, Label[] memberLabels, Label throwDuplicateMemberLabel, LocalBuilder memberIndexLocal) { @@ -428,6 +439,7 @@ private void ResetExpectedElements(BitFlagsGenerator expectedElements, int index expectedElements.Store(index, false); } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] private void ReadISerializable(ClassDataContract classContract) { ConstructorInfo? ctor = classContract.UnderlyingType.GetConstructor(Globals.ScanAllMembers, JsonFormatGeneratorStatics.SerInfoCtorArgs); @@ -441,6 +453,7 @@ private void ReadISerializable(ClassDataContract classContract) _ilg.Call(ctor); } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] private LocalBuilder ReadValue(Type type, string name) { LocalBuilder value = _ilg.DeclareLocal(type, "valueRead"); @@ -547,6 +560,7 @@ private void InternalDeserialize(LocalBuilder value, Type type, string name) _ilg.Stloc(value); } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] private void WrapNullableObject(LocalBuilder innerValue, LocalBuilder outerValue, int nullables) { Type innerType = innerValue.LocalType, outerType = outerValue.LocalType; @@ -561,6 +575,7 @@ private void WrapNullableObject(LocalBuilder innerValue, LocalBuilder outerValue _ilg.Call(outerType.GetConstructor(new Type[] { innerType })!); } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] private void ReadCollection(CollectionDataContract collectionContract) { Type type = collectionContract.UnderlyingType; @@ -686,6 +701,7 @@ private void ReadCollection(CollectionDataContract collectionContract) } } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] private void ReadSimpleDictionary(CollectionDataContract collectionContract, Type keyValueType) { Type[] keyValueTypes = keyValueType.GetGenericArguments(); @@ -778,6 +794,7 @@ private void ReadSimpleDictionary(CollectionDataContract collectionContract, Typ } } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] private void ReadGetOnlyCollection(CollectionDataContract collectionContract) { Type type = collectionContract.UnderlyingType; @@ -916,6 +933,7 @@ private bool TryReadPrimitiveArray(Type itemType) return false; } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] private LocalBuilder ReadCollectionItem(CollectionDataContract collectionContract, Type itemType) { if (collectionContract.Kind == CollectionKind.Dictionary || collectionContract.Kind == CollectionKind.GenericDictionary) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatWriterGenerator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatWriterGenerator.cs index e47e30fd673612..a39e36802db466 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatWriterGenerator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatWriterGenerator.cs @@ -9,6 +9,7 @@ namespace System.Runtime.Serialization.Json using System; using System.Collections; using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Reflection.Emit; using System.Security; @@ -26,16 +27,26 @@ public JsonFormatWriterGenerator() _helper = new CriticalHelper(); } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] internal JsonFormatClassWriterDelegate GenerateClassWriter(ClassDataContract classContract) { return _helper.GenerateClassWriter(classContract); } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] internal JsonFormatCollectionWriterDelegate GenerateCollectionWriter(CollectionDataContract collectionContract) { return _helper.GenerateCollectionWriter(collectionContract); } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", + Justification = "The trimmer will never remove the Invoke method from delegates.")] + internal static MethodInfo GetInvokeMethod(Type delegateType) + { + Debug.Assert(typeof(Delegate).IsAssignableFrom(delegateType)); + return delegateType.GetMethod("Invoke")!; + } + private sealed class CriticalHelper { private CodeGenerator _ilg = null!; // initialized in GenerateXXXWriter @@ -49,6 +60,7 @@ private sealed class CriticalHelper private int _typeIndex = 1; private int _childElementIndex; + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] internal JsonFormatClassWriterDelegate GenerateClassWriter(ClassDataContract classContract) { _ilg = new CodeGenerator(); @@ -74,6 +86,7 @@ internal JsonFormatClassWriterDelegate GenerateClassWriter(ClassDataContract cla return (JsonFormatClassWriterDelegate)_ilg.EndMethod(); } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] internal JsonFormatCollectionWriterDelegate GenerateCollectionWriter(CollectionDataContract collectionContract) { _ilg = new CodeGenerator(); @@ -100,8 +113,7 @@ internal JsonFormatCollectionWriterDelegate GenerateCollectionWriter(CollectionD private void BeginMethod(CodeGenerator ilg, string methodName, Type delegateType, bool allowPrivateMemberAccess) { - - MethodInfo signature = delegateType.GetMethod("Invoke")!; + MethodInfo signature = GetInvokeMethod(delegateType); ParameterInfo[] parameters = signature.GetParameters(); Type[] paramTypes = new Type[parameters.Length]; for (int i = 0; i < parameters.Length; i++) @@ -111,6 +123,7 @@ private void BeginMethod(CodeGenerator ilg, string methodName, Type delegateType ilg.BeginMethod(dynamicMethod, delegateType, methodName, paramTypes, allowPrivateMemberAccess); } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] private void InitArgs(Type objType) { _xmlWriterArg = _ilg.GetArg(0); @@ -272,6 +285,7 @@ private LocalBuilder LoadMemberValue(DataMember member) return memberValue; } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] private void WriteCollection(CollectionDataContract collectionContract) { LocalBuilder itemName = _ilg.DeclareLocal(typeof(XmlDictionaryString), "itemName"); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonObjectDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonObjectDataContract.cs index 034d57d757da05..2da99f1eff5d2a 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonObjectDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonObjectDataContract.cs @@ -4,16 +4,19 @@ using System.Xml; using System.Runtime.Serialization; using System.Globalization; +using System.Diagnostics.CodeAnalysis; namespace System.Runtime.Serialization.Json { internal sealed class JsonObjectDataContract : JsonDataContract { + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public JsonObjectDataContract(DataContract traditionalDataContract) : base(traditionalDataContract) { } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public override object? ReadJsonValueCore(XmlReaderDelegator jsonReader, XmlObjectSerializerReadContextComplexJson? context) { object? obj; @@ -53,6 +56,7 @@ public JsonObjectDataContract(DataContract traditionalDataContract) return obj; } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public override void WriteJsonValueCore(XmlWriterDelegator jsonWriter, object obj, XmlObjectSerializerWriteContextComplexJson? context, RuntimeTypeHandle declaredTypeHandle) { jsonWriter.WriteAttributeString(null, JsonGlobals.typeString, null, JsonGlobals.objectString); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonQNameDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonQNameDataContract.cs index 57c61347c83a26..aa6d2f137a54cc 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonQNameDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonQNameDataContract.cs @@ -6,16 +6,19 @@ using System.Text; using System.Diagnostics; using System.Xml; +using System.Diagnostics.CodeAnalysis; namespace System.Runtime.Serialization.Json { internal sealed class JsonQNameDataContract : JsonDataContract { + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public JsonQNameDataContract(QNameDataContract traditionalQNameDataContract) : base(traditionalQNameDataContract) { } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public override object? ReadJsonValueCore(XmlReaderDelegator jsonReader, XmlObjectSerializerReadContextComplexJson? context) { if (context == null) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonStringDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonStringDataContract.cs index 89365be7c76263..19c1f28182829f 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonStringDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonStringDataContract.cs @@ -6,16 +6,19 @@ using System.Text; using System.Diagnostics; using System.Xml; +using System.Diagnostics.CodeAnalysis; namespace System.Runtime.Serialization.Json { internal sealed class JsonStringDataContract : JsonDataContract { + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public JsonStringDataContract(StringDataContract traditionalStringDataContract) : base(traditionalStringDataContract) { } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public override object? ReadJsonValueCore(XmlReaderDelegator jsonReader, XmlObjectSerializerReadContextComplexJson? context) { if (context == null) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonUriDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonUriDataContract.cs index de4dce0cf7fced..7cc8601bab92f3 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonUriDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonUriDataContract.cs @@ -1,21 +1,19 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; -using System.Text; -using System.Diagnostics; -using System.Xml; +using System.Diagnostics.CodeAnalysis; namespace System.Runtime.Serialization.Json { internal sealed class JsonUriDataContract : JsonDataContract { + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public JsonUriDataContract(UriDataContract traditionalUriDataContract) : base(traditionalUriDataContract) { } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public override object? ReadJsonValueCore(XmlReaderDelegator jsonReader, XmlObjectSerializerReadContextComplexJson? context) { if (context == null) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonXmlDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonXmlDataContract.cs index 12044fb8b2ec42..4fc9ac93da4ccd 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonXmlDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonXmlDataContract.cs @@ -1,20 +1,23 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Xml; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Text; +using System.Xml; namespace System.Runtime.Serialization.Json { internal sealed class JsonXmlDataContract : JsonDataContract { + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public JsonXmlDataContract(XmlDataContract traditionalXmlDataContract) : base(traditionalXmlDataContract) { } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public override object? ReadJsonValueCore(XmlReaderDelegator jsonReader, XmlObjectSerializerReadContextComplexJson? context) { string xmlContent = jsonReader.ReadElementContentAsString(); @@ -40,6 +43,7 @@ public JsonXmlDataContract(XmlDataContract traditionalXmlDataContract) return xmlValue; } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public override void WriteJsonValueCore(XmlWriterDelegator jsonWriter, object obj, XmlObjectSerializerWriteContextComplexJson? context, RuntimeTypeHandle declaredTypeHandle) { DataContractSerializer dataContractSerializer = new DataContractSerializer(Type.GetTypeFromHandle(declaredTypeHandle), diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatReader.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatReader.cs index dae0e1b19128f8..e27857f03411fe 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatReader.cs @@ -5,6 +5,7 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Text; @@ -102,6 +103,7 @@ protected override string GetCollectionContractNamespace(CollectionDataContract return string.Empty; } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] protected override object? ReflectionReadDictionaryItem(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, CollectionDataContract collectionContract) { var jsonContext = context as XmlObjectSerializerReadContextComplexJson; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatWriter.cs index 2c1909e0078703..c67784eed42c8e 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatWriter.cs @@ -5,6 +5,7 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Text; @@ -22,6 +23,7 @@ public void ReflectionWriteClass(XmlWriterDelegator xmlWriter, object obj, XmlOb _reflectionClassWriter.ReflectionWriteClass(xmlWriter, obj, context, classContract, memberNames); } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] public void ReflectionWriteCollection(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContextComplexJson context, CollectionDataContract collectionContract) { JsonWriterDelegator? jsonWriter = xmlWriter as JsonWriterDelegator; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerReadContextComplexJson.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerReadContextComplexJson.cs index f5f9b404b46a52..c84405311d4094 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerReadContextComplexJson.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerReadContextComplexJson.cs @@ -34,6 +34,7 @@ internal static XmlObjectSerializerReadContextComplexJson CreateContext(DataCont return new XmlObjectSerializerReadContextComplexJson(serializer, rootTypeDataContract); } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] protected override object? ReadDataContractValue(DataContract dataContract, XmlReaderDelegator reader) { return DataContractJsonSerializerImpl.ReadJsonValue(dataContract, reader, this); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerWriteContextComplexJson.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerWriteContextComplexJson.cs index 996e2c3915f938..ef784e7499c4cb 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerWriteContextComplexJson.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerWriteContextComplexJson.cs @@ -151,6 +151,7 @@ private void WriteTypeInfo(XmlWriterDelegator writer, string typeInformation) writer.WriteAttributeString(null, JsonGlobals.serverTypeString, null, typeInformation); } + [RequiresUnreferencedCode(DataContractJsonSerializer.SerializerTrimmerWarning)] protected override void WriteDataContractValue(DataContract dataContract, XmlWriterDelegator xmlWriter, object obj, RuntimeTypeHandle declaredTypeHandle) { JsonDataContract jsonDataContract = JsonDataContract.GetJsonDataContract(dataContract); diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/HelperMarshal.cs b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/HelperMarshal.cs index 73af784e94dba7..4dbd576cc6ddff 100644 --- a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/HelperMarshal.cs +++ b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/HelperMarshal.cs @@ -72,6 +72,16 @@ private static string StoreArgumentAndReturnLiteral(string s) return "1"; } + private static string StoreAndReturnNew(string s) + { + var sb = new System.Text.StringBuilder(); + sb.Append("Got:"); + sb.Append(' '); + sb.Append(s); + _stringResource = sb.ToString(); + return _stringResource; + } + internal static string _marshalledString; private static string InvokeMarshalString() { diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/MarshalTests.cs b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/MarshalTests.cs index 52d12c7598c8f2..b70e07511c4852 100644 --- a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/MarshalTests.cs +++ b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/MarshalTests.cs @@ -85,6 +85,22 @@ public static void MarshalStringToCS() Assert.Equal("hello", HelperMarshal._stringResource); } + [Fact] + public static void MarshalUnicodeStringToCS() + { + HelperMarshal._stringResource = null; + Runtime.InvokeJS("App.call_test_method(\"StoreAndReturnNew\", [' '+\"\u0050\u0159\u00ed\u006c\u0069\u0161\u0020\u017e\u006c\u0075\u0165\u006f\u0075\u010d\u006b\u00fd\u0020\u006b\u016f\u0148\u202f\u00fa\u0070\u011b\u006c\u0020\u010f\u00e1\u0062\u0065\u006c\u0073\u006b\u00e9\u0020\u00f3\u0064\u0079\"])"); + Assert.Equal("Got: \u0050\u0159\u00ed\u006c\u0069\u0161\u0020\u017e\u006c\u0075\u0165\u006f\u0075\u010d\u006b\u00fd\u0020\u006b\u016f\u0148\u202f\u00fa\u0070\u011b\u006c\u0020\u010f\u00e1\u0062\u0065\u006c\u0073\u006b\u00e9\u0020\u00f3\u0064\u0079", HelperMarshal._stringResource); + + HelperMarshal._stringResource = null; + Runtime.InvokeJS("App.call_test_method(\"StoreAndReturnNew\", [' '+\"\uFEFF\u0000\uFFFE\"])"); + Assert.Equal("Got: \uFEFF\0\uFFFE", HelperMarshal._stringResource); + + HelperMarshal._stringResource = null; + Runtime.InvokeJS("App.call_test_method(\"StoreAndReturnNew\", [' '+\"\u02F3o\u0302\u0303\u0308\u0930\u0903\u0951\"])"); + Assert.Equal("Got: \u02F3o\u0302\u0303\u0308\u0930\u0903\u0951", HelperMarshal._stringResource); + } + [Fact] public static void MarshalNullStringToCS() { diff --git a/src/libraries/System.Reflection.DispatchProxy/src/System/Reflection/DispatchProxyGenerator.cs b/src/libraries/System.Reflection.DispatchProxy/src/System/Reflection/DispatchProxyGenerator.cs index 6cb945ca3a3820..643265d939ab6b 100644 --- a/src/libraries/System.Reflection.DispatchProxy/src/System/Reflection/DispatchProxyGenerator.cs +++ b/src/libraries/System.Reflection.DispatchProxy/src/System/Reflection/DispatchProxyGenerator.cs @@ -394,9 +394,19 @@ internal void AddInterfaceImpl([DynamicallyAccessedMembers(DynamicallyAccessedMe private MethodBuilder AddMethodImpl(MethodInfo mi, int methodInfoIndex) { ParameterInfo[] parameters = mi.GetParameters(); - Type[] paramTypes = ParamTypes(parameters, false); + Type[] paramTypes = new Type[parameters.Length]; + Type[][] paramReqMods = new Type[paramTypes.Length][]; + + for (int i = 0; i < parameters.Length; i++) + { + paramTypes[i] = parameters[i].ParameterType; + paramReqMods[i] = parameters[i].GetRequiredCustomModifiers(); + } + + MethodBuilder mdb = _tb.DefineMethod(mi.Name, MethodAttributes.Public | MethodAttributes.Virtual, CallingConventions.Standard, + mi.ReturnType, null, null, + paramTypes, paramReqMods, null); - MethodBuilder mdb = _tb.DefineMethod(mi.Name, MethodAttributes.Public | MethodAttributes.Virtual, mi.ReturnType, paramTypes); if (mi.ContainsGenericParameters) { Type[] ts = mi.GetGenericArguments(); @@ -417,7 +427,7 @@ private MethodBuilder AddMethodImpl(MethodInfo mi, int methodInfoIndex) // object[] args = new object[paramCount]; il.Emit(OpCodes.Nop); - GenericArray argsArr = new GenericArray(il, ParamTypes(parameters, true).Length); + GenericArray argsArr = new GenericArray(il, parameters.Length); for (int i = 0; i < parameters.Length; i++) { @@ -502,18 +512,6 @@ private MethodBuilder AddMethodImpl(MethodInfo mi, int methodInfoIndex) return mdb; } - private static Type[] ParamTypes(ParameterInfo[] parms, bool noByRef) - { - Type[] types = new Type[parms.Length]; - for (int i = 0; i < parms.Length; i++) - { - types[i] = parms[i].ParameterType; - if (noByRef && types[i].IsByRef) - types[i] = types[i].GetElementType()!; - } - return types; - } - // TypeCode does not exist in ProjectK or ProjectN. // This lookup method was copied from PortableLibraryThunks\Internal\PortableLibraryThunks\System\TypeThunks.cs // but returns the integer value equivalent to its TypeCode enum. diff --git a/src/libraries/System.Reflection.DispatchProxy/tests/DispatchProxyTests.cs b/src/libraries/System.Reflection.DispatchProxy/tests/DispatchProxyTests.cs index dd0563bf4800b4..cb5edc26d5d5fc 100644 --- a/src/libraries/System.Reflection.DispatchProxy/tests/DispatchProxyTests.cs +++ b/src/libraries/System.Reflection.DispatchProxy/tests/DispatchProxyTests.cs @@ -603,6 +603,7 @@ public static void Invoke_Ref_Out_In_Method() testRefOutInInvocation(p => p.Out(out _), null); testRefOutInInvocation(p => p.OutAttribute(value), "Hello"); testRefOutInInvocation(p => p.Ref(ref value), "Hello"); + testRefOutInInvocation(p => p.In(in value), "Hello"); } private static void testRefOutInInvocation(Action invocation, string expected) diff --git a/src/libraries/System.Reflection.DispatchProxy/tests/TestTypes.cs b/src/libraries/System.Reflection.DispatchProxy/tests/TestTypes.cs index d4fa174fd29814..dca9f2d266c8d2 100644 --- a/src/libraries/System.Reflection.DispatchProxy/tests/TestTypes.cs +++ b/src/libraries/System.Reflection.DispatchProxy/tests/TestTypes.cs @@ -19,6 +19,7 @@ public interface TestType_IHelloService public interface TestType_IOut_Ref { + void In(in string message); void Out(out string message); void Ref(ref string message); void InAttribute([In] string message); diff --git a/src/libraries/System.Reflection.Metadata/tests/PortableExecutable/PEBuilderTests.cs b/src/libraries/System.Reflection.Metadata/tests/PortableExecutable/PEBuilderTests.cs index dd7d036f31bd9a..763e26dbc1811c 100644 --- a/src/libraries/System.Reflection.Metadata/tests/PortableExecutable/PEBuilderTests.cs +++ b/src/libraries/System.Reflection.Metadata/tests/PortableExecutable/PEBuilderTests.cs @@ -134,7 +134,7 @@ public void BasicValidation(Machine machine) } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] // System.Security.Cryptography isn't supported on browser + [SkipOnPlatform(TestPlatforms.Browser, "System.Security.Cryptography isn't supported on browser")] public void BasicValidationSigned() { using (var peStream = new MemoryStream()) @@ -655,7 +655,7 @@ private static IEnumerable GetBlobRanges(BlobBuilder builder, IEnumerabl } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] // System.Security.Cryptography isn't supported on browser + [SkipOnPlatform(TestPlatforms.Browser, "System.Security.Cryptography isn't supported on browser")] public void Checksum() { Assert.True(TestChecksumAndAuthenticodeSignature(new MemoryStream(Misc.Signed), Misc.KeyPair)); @@ -663,7 +663,7 @@ public void Checksum() } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] // System.Security.Cryptography isn't supported on browser + [SkipOnPlatform(TestPlatforms.Browser, "System.Security.Cryptography isn't supported on browser")] public void ChecksumFXAssemblies() { var paths = new[] diff --git a/src/libraries/System.Reflection/tests/AssemblyTests.cs b/src/libraries/System.Reflection/tests/AssemblyTests.cs index 1892109d240f1c..7424e2cd07f2bc 100644 --- a/src/libraries/System.Reflection/tests/AssemblyTests.cs +++ b/src/libraries/System.Reflection/tests/AssemblyTests.cs @@ -144,7 +144,7 @@ public void ExportedTypes(Type type, bool expected) } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] // entry assembly won't be xunit.console on browser + [SkipOnPlatform(TestPlatforms.Browser, "entry assembly won't be xunit.console on browser")] public void GetEntryAssembly() { Assert.NotNull(Assembly.GetEntryAssembly()); diff --git a/src/libraries/System.Runtime.Caching/tests/System.Runtime.Caching/HostFileChangeMonitorTest.cs b/src/libraries/System.Runtime.Caching/tests/System.Runtime.Caching/HostFileChangeMonitorTest.cs index ca3d5ffe757af5..6d629139436289 100644 --- a/src/libraries/System.Runtime.Caching/tests/System.Runtime.Caching/HostFileChangeMonitorTest.cs +++ b/src/libraries/System.Runtime.Caching/tests/System.Runtime.Caching/HostFileChangeMonitorTest.cs @@ -41,7 +41,7 @@ namespace MonoTests.System.Runtime.Caching { - [SkipOnMono("HostFileChangeMonitor is not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "HostFileChangeMonitor is not supported on Browser")] public class HostFileChangeMonitorTest { [Fact] diff --git a/src/libraries/System.Runtime.Extensions/tests/System/AppDomainTests.cs b/src/libraries/System.Runtime.Extensions/tests/System/AppDomainTests.cs index 1101f747efccdd..449b8c7f040e82 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/AppDomainTests.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/AppDomainTests.cs @@ -44,7 +44,7 @@ public void RelativeSearchPath_Is_Null() } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] // throws pNSE + [SkipOnPlatform(TestPlatforms.Browser, "throws pNSE")] [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] [ActiveIssue("https://github.com/dotnet/runtime/issues/49868", TestPlatforms.Android)] public void TargetFrameworkTest() @@ -88,7 +88,7 @@ public void UnhandledException_NotCalled_When_Handled() } [ActiveIssue("https://github.com/dotnet/runtime/issues/18984")] - [PlatformSpecific(~TestPlatforms.OSX)] // Unhandled exception on a separate process causes xunit to crash on osx + [SkipOnPlatform(TestPlatforms.OSX, "Unhandled exception on a separate process causes xunit to crash on osx")] [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] public void UnhandledException_Called() { @@ -361,7 +361,7 @@ public void Load() } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public void LoadBytes() { Assembly assembly = typeof(AppDomainTests).Assembly; @@ -376,7 +376,7 @@ public void ReflectionOnlyGetAssemblies() } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] // Throws PNSE + [SkipOnPlatform(TestPlatforms.Browser, "Throws PNSE")] public void MonitoringIsEnabled() { Assert.True(AppDomain.MonitoringIsEnabled); diff --git a/src/libraries/System.Runtime.Extensions/tests/System/Environment.Exit.cs b/src/libraries/System.Runtime.Extensions/tests/System/Environment.Exit.cs index 2b49f421fb3418..8ca85743f417c9 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/Environment.Exit.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/Environment.Exit.cs @@ -42,7 +42,7 @@ public static void ExitCode_Roundtrips(int exitCode) [InlineData(1)] // setting ExitCode and exiting Main [InlineData(2)] // setting ExitCode both from Main and from an Unloading event handler. [InlineData(3)] // using Exit(exitCode) - [PlatformSpecific(~TestPlatforms.Browser)] // throws PNSE + [SkipOnPlatform(TestPlatforms.Browser, "throws PNSE")] [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] [ActiveIssue("https://github.com/dotnet/runtime/issues/49868", TestPlatforms.Android)] public static void ExitCode_VoidMainAppReturnsSetValue(int mode) diff --git a/src/libraries/System.Runtime.Extensions/tests/System/Random.cs b/src/libraries/System.Runtime.Extensions/tests/System/Random.cs index 8a6491aef05308..1cc1ccc21ec97e 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/Random.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/Random.cs @@ -130,6 +130,31 @@ public void Next_IntInt_AllValuesWithinSmallRangeHit(bool derived, bool seeded) Assert.DoesNotContain(44, hs); } + public static IEnumerable Next_IntInt_Next_IntInt_AllValuesAreWithinRange_MemberData() => + from derived in new[] { false, true } + from seeded in new[] { false, true } + from (int min, int max) pair in new[] + { + (1, 2), + (-10, -3), + (0, int.MaxValue), + (-1, int.MaxValue), + (int.MinValue, 0), + (int.MinValue, int.MaxValue), + } + select new object[] { derived, seeded, pair.min, pair.max }; + + [Theory] + [MemberData(nameof(Next_IntInt_Next_IntInt_AllValuesAreWithinRange_MemberData))] + public void Next_IntInt_Next_IntInt_AllValuesAreWithinRange(bool derived, bool seeded, int min, int max) + { + Random r = Create(derived, seeded); + for (int i = 0; i < 100; i++) + { + Assert.InRange(r.Next(min, max), min, max - 1); + } + } + [Theory] [InlineData(false, false)] [InlineData(false, true)] @@ -178,6 +203,31 @@ public void Next_LongLong_AllValuesWithinSmallRangeHit(bool derived, bool seeded Assert.DoesNotContain(44L, hs); } + public static IEnumerable Next_LongLong_Next_IntInt_AllValuesAreWithinRange_MemberData() => + from derived in new[] { false, true } + from seeded in new[] { false, true } + from (long min, long max) pair in new[] + { + (1L, 2L), + (0L, long.MaxValue), + (2147483648, 2147483658), + (-1L, long.MaxValue), + (long.MinValue, 0L), + (long.MinValue, long.MaxValue), + } + select new object[] { derived, seeded, pair.min, pair.max }; + + [Theory] + [MemberData(nameof(Next_LongLong_Next_IntInt_AllValuesAreWithinRange_MemberData))] + public void Next_LongLong_Next_IntInt_AllValuesAreWithinRange(bool derived, bool seeded, long min, long max) + { + Random r = Create(derived, seeded); + for (int i = 0; i < 100; i++) + { + Assert.InRange(r.NextInt64(min, max), min, max - 1); + } + } + [Theory] [InlineData(false)] [InlineData(true)] @@ -502,7 +552,7 @@ public void Xoshiro_AlgorithmBehavesAsExpected() Assert.Equal(12, randOuter.Next(0, 42)); Assert.Equal(7234, randOuter.Next(42, 12345)); Assert.Equal(2147483642, randOuter.Next(int.MaxValue - 5, int.MaxValue)); - Assert.Equal(1981894504, randOuter.Next(int.MinValue, int.MaxValue)); + Assert.Equal(-1236260882, randOuter.Next(int.MinValue, int.MaxValue)); Assert.Equal(3644728249650840822, randOuter.NextInt64()); Assert.Equal(2809750975933744783, randOuter.NextInt64()); diff --git a/src/libraries/System.Runtime.Extensions/tests/System/Reflection/AssemblyNameProxyTests.cs b/src/libraries/System.Runtime.Extensions/tests/System/Reflection/AssemblyNameProxyTests.cs index 39059fa7eba11f..1a02e4686f2e8c 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/Reflection/AssemblyNameProxyTests.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/Reflection/AssemblyNameProxyTests.cs @@ -13,7 +13,7 @@ namespace System.Reflection.Tests public static class AssemblyNameProxyTests { [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] public static void GetAssemblyName_AssemblyNameProxy() { AssemblyNameProxy anp = new AssemblyNameProxy(); diff --git a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/DescriptionNameTests.cs b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/DescriptionNameTests.cs index 41d11ebdc96a96..4568724fc9947f 100644 --- a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/DescriptionNameTests.cs +++ b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/DescriptionNameTests.cs @@ -19,7 +19,7 @@ public class DescriptionNameTests private static readonly bool s_isInHelix = Environment.GetEnvironmentVariables().Keys.Cast().Where(key => key.StartsWith("HELIX")).Any(); [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] // throws PNSE when binariesLocation is not an empty string. + [SkipOnPlatform(TestPlatforms.Browser, "throws PNSE when binariesLocation is not an empty string.")] public void DumpRuntimeInformationToConsole() { if (s_dumpedRuntimeInfo || !s_isInHelix) @@ -164,7 +164,7 @@ public void DumpRuntimeInformationToConsole() [Fact] [OuterLoop] - [PlatformSpecific(~TestPlatforms.Browser)] // throws PNSE when binariesLocation is not an empty string. + [SkipOnPlatform(TestPlatforms.Browser, "throws PNSE when binariesLocation is not an empty string.")] public void DumpRuntimeInformationToConsoleOuter() { // Outer loop runs don't run inner loop tests. 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 87757e9fde531b..5fcb17353156e9 100644 --- a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs +++ b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs @@ -492,7 +492,6 @@ public static partial class Marshal { public static readonly int SystemDefaultCharSize; public static readonly int SystemMaxDBCSCharSize; - [System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] public static int AddRef(System.IntPtr pUnk) { throw null; } public static System.IntPtr AllocCoTaskMem(int cb) { throw null; } public static System.IntPtr AllocHGlobal(int cb) { throw null; } @@ -625,7 +624,6 @@ public static void PtrToStructure(System.IntPtr ptr, object structure) { } public static object? PtrToStructure(System.IntPtr ptr, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] System.Type structureType) { throw null; } public static T? PtrToStructure<[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]T>(System.IntPtr ptr) { throw null; } public static void PtrToStructure(System.IntPtr ptr, [System.Diagnostics.CodeAnalysis.DisallowNullAttribute] T structure) { } - [System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] public static int QueryInterface(System.IntPtr pUnk, ref System.Guid iid, out System.IntPtr ppv) { throw null; } public static byte ReadByte(System.IntPtr ptr) { throw null; } public static byte ReadByte(System.IntPtr ptr, int ofs) { throw null; } @@ -654,7 +652,6 @@ public static void PtrToStructure(System.IntPtr ptr, [System.Diagnostics.Code public static System.IntPtr ReadIntPtr(object ptr, int ofs) { throw null; } public static System.IntPtr ReAllocCoTaskMem(System.IntPtr pv, int cb) { throw null; } public static System.IntPtr ReAllocHGlobal(System.IntPtr pv, System.IntPtr cb) { throw null; } - [System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] public static int Release(System.IntPtr pUnk) { throw null; } [System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] public static int ReleaseComObject(object o) { throw null; } diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/AddRefTests.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/AddRefTests.cs index b0363961023645..c8cd89168b3526 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/AddRefTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/AddRefTests.cs @@ -29,14 +29,6 @@ public void AddRef_ValidPointer_Success() } [Fact] - [PlatformSpecific(TestPlatforms.AnyUnix)] - public void AddRef_Unix_ThrowsPlatformNotSupportedException() - { - Assert.Throws(() => Marshal.AddRef(IntPtr.Zero)); - } - - [Fact] - [PlatformSpecific(TestPlatforms.Windows)] public void AddRef_ZeroPointer_ThrowsArgumentNullException() { AssertExtensions.Throws("pUnk", () => Marshal.AddRef(IntPtr.Zero)); diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/QueryInterfaceTests.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/QueryInterfaceTests.cs index 15e504b86fd00b..fbb7ca8c0fa6bd 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/QueryInterfaceTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/QueryInterfaceTests.cs @@ -124,15 +124,6 @@ public void QueryInterface_NoSuchInterface_Success(object o, string iidString) } [Fact] - [PlatformSpecific(TestPlatforms.AnyUnix)] - public void QueryInterface_Unix_ThrowsPlatformNotSupportedException() - { - Guid iid = Guid.Empty; - Assert.Throws(() => Marshal.QueryInterface(IntPtr.Zero, ref iid, out IntPtr ppv)); - } - - [Fact] - [PlatformSpecific(TestPlatforms.Windows)] public void QueryInterface_ZeroPointer_ThrowsArgumentNullException() { Guid iid = Guid.Empty; diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/ReleaseTests.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/ReleaseTests.cs index 9081a71103b178..211270c6995785 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/ReleaseTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/ReleaseTests.cs @@ -30,14 +30,6 @@ public void Release_ValidPointer_Success() } [Fact] - [PlatformSpecific(TestPlatforms.AnyUnix)] - public void Release_Unix_ThrowsPlatformNotSupportedException() - { - Assert.Throws(() => Marshal.Release(IntPtr.Zero)); - } - - [Fact] - [PlatformSpecific(TestPlatforms.Windows)] public void Release_ZeroPointer_ThrowsArgumentNullException() { AssertExtensions.Throws("pUnk", () => Marshal.Release(IntPtr.Zero)); diff --git a/src/libraries/System.Runtime.Loader/ref/System.Runtime.Loader.cs b/src/libraries/System.Runtime.Loader/ref/System.Runtime.Loader.cs index 8279cfa8b9c43e..e42a0642b32ea9 100644 --- a/src/libraries/System.Runtime.Loader/ref/System.Runtime.Loader.cs +++ b/src/libraries/System.Runtime.Loader/ref/System.Runtime.Loader.cs @@ -12,6 +12,13 @@ public static partial class AssemblyExtensions public unsafe static bool TryGetRawMetadata(this System.Reflection.Assembly assembly, out byte* blob, out int length) { throw null; } public static void ApplyUpdate(Assembly assembly, ReadOnlySpan metadataDelta, ReadOnlySpan ilDelta, ReadOnlySpan pdbDelta) { throw null; } } + [System.AttributeUsage(System.AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class MetadataUpdateHandlerAttribute : System.Attribute + { + public MetadataUpdateHandlerAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type handlerType) { } + [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] + public System.Type HandlerType { get { throw null; } } + } } namespace System.Runtime.Loader { diff --git a/src/libraries/System.Runtime.Loader/tests/AssemblyLoadContextTest.cs b/src/libraries/System.Runtime.Loader/tests/AssemblyLoadContextTest.cs index 8a33d9ac71af33..c554f37d441e42 100644 --- a/src/libraries/System.Runtime.Loader/tests/AssemblyLoadContextTest.cs +++ b/src/libraries/System.Runtime.Loader/tests/AssemblyLoadContextTest.cs @@ -107,7 +107,7 @@ public static void LoadFromAssemblyName_AssemblyNotFound() } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] // Corelib does not exist on disc for Browser builds + [SkipOnPlatform(TestPlatforms.Browser, "Corelib does not exist on disc for Browser builds")] public static void LoadFromAssemblyName_ValidTrustedPlatformAssembly() { var asmName = typeof(System.Linq.Enumerable).Assembly.GetName(); diff --git a/src/libraries/System.Runtime.Loader/tests/MetadataUpdateHandlerAttributeTest.cs b/src/libraries/System.Runtime.Loader/tests/MetadataUpdateHandlerAttributeTest.cs new file mode 100644 index 00000000000000..59c61d7d8a65df --- /dev/null +++ b/src/libraries/System.Runtime.Loader/tests/MetadataUpdateHandlerAttributeTest.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.Reflection.Metadata +{ + public class MetadataUpdateHandlerAttributeTest + { + [Fact] + public void Ctor_RoundtripType() + { + Type t = typeof(MetadataUpdateHandlerAttributeTest); + var a = new MetadataUpdateHandlerAttribute(t); + Assert.Same(t, a.HandlerType); + } + } +} diff --git a/src/libraries/System.Runtime.Loader/tests/System.Runtime.Loader.Tests.csproj b/src/libraries/System.Runtime.Loader/tests/System.Runtime.Loader.Tests.csproj index 11c1b7577fff9d..1ab71b1f41abfe 100644 --- a/src/libraries/System.Runtime.Loader/tests/System.Runtime.Loader.Tests.csproj +++ b/src/libraries/System.Runtime.Loader/tests/System.Runtime.Loader.Tests.csproj @@ -13,6 +13,7 @@ + diff --git a/src/libraries/System.Runtime.Numerics/tests/BigInteger/GetBitLengthTests.cs b/src/libraries/System.Runtime.Numerics/tests/BigInteger/GetBitLengthTests.cs index fccdfe475f18ba..242a2e2382ca86 100644 --- a/src/libraries/System.Runtime.Numerics/tests/BigInteger/GetBitLengthTests.cs +++ b/src/libraries/System.Runtime.Numerics/tests/BigInteger/GetBitLengthTests.cs @@ -39,7 +39,7 @@ public static void RunGetBitLengthTests() } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] // OOM on browser due to large array allocations + [SkipOnPlatform(TestPlatforms.Browser, "OOM on browser due to large array allocations")] public static void RunGetBitLengthTestsLarge() { // Very large cases diff --git a/src/libraries/System.Runtime.Serialization.Formatters/tests/BinaryFormatterTests.cs b/src/libraries/System.Runtime.Serialization.Formatters/tests/BinaryFormatterTests.cs index 155346f681de81..b4076482fcb8f0 100644 --- a/src/libraries/System.Runtime.Serialization.Formatters/tests/BinaryFormatterTests.cs +++ b/src/libraries/System.Runtime.Serialization.Formatters/tests/BinaryFormatterTests.cs @@ -51,7 +51,7 @@ public void SerializeHugeObjectGraphs(int limit) [SkipOnCoreClr("Takes too long on Checked", RuntimeConfiguration.Checked)] [ActiveIssue("https://github.com/dotnet/runtime/issues/34008", TestPlatforms.Linux, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] [ActiveIssue("https://github.com/dotnet/runtime/issues/34753", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Takes too long on Browser.")] [MemberData(nameof(BasicObjectsRoundtrip_MemberData))] public void ValidateBasicObjectsRoundtrip(object obj, FormatterAssemblyStyle assemblyFormat, TypeFilterLevel filterLevel, FormatterTypeStyle typeFormat) { @@ -201,7 +201,7 @@ public void ValidateDeserializationOfObjectWithGenericTypeWhichGenericArgumentHa [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/34008", TestPlatforms.Linux, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] [ActiveIssue("https://github.com/dotnet/runtime/issues/34753", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] - [PlatformSpecific(~TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Takes too long on Browser.")] public void RoundtripManyObjectsInOneStream() { object[][] objects = SerializableObjects_MemberData().ToArray(); diff --git a/src/libraries/System.Runtime.Serialization.Json/ref/System.Runtime.Serialization.Json.cs b/src/libraries/System.Runtime.Serialization.Json/ref/System.Runtime.Serialization.Json.cs index e9dee409abc334..6781f4925fba07 100644 --- a/src/libraries/System.Runtime.Serialization.Json/ref/System.Runtime.Serialization.Json.cs +++ b/src/libraries/System.Runtime.Serialization.Json/ref/System.Runtime.Serialization.Json.cs @@ -25,12 +25,19 @@ namespace System.Runtime.Serialization.Json { public sealed partial class DataContractJsonSerializer : System.Runtime.Serialization.XmlObjectSerializer { + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Json Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] public DataContractJsonSerializer(System.Type type) { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Json Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] public DataContractJsonSerializer(System.Type type, System.Collections.Generic.IEnumerable? knownTypes) { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Json Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] public DataContractJsonSerializer(System.Type type, System.Runtime.Serialization.Json.DataContractJsonSerializerSettings? settings) { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Json Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] public DataContractJsonSerializer(System.Type type, string? rootName) { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Json Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] public DataContractJsonSerializer(System.Type type, string? rootName, System.Collections.Generic.IEnumerable? knownTypes) { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Json Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] public DataContractJsonSerializer(System.Type type, System.Xml.XmlDictionaryString? rootName) { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Json Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved.")] public DataContractJsonSerializer(System.Type type, System.Xml.XmlDictionaryString? rootName, System.Collections.Generic.IEnumerable? knownTypes) { } public System.Runtime.Serialization.DateTimeFormat? DateTimeFormat { get { throw null; } } public System.Runtime.Serialization.EmitTypeInformation EmitTypeInformation { get { throw null; } } diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 6292cc6b7e7db0..3249df05c058a7 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -3107,6 +3107,8 @@ public void GetObjectData(System.Runtime.Serialization.SerializationInfo info, S public static bool IsIOSVersionAtLeast(int major, int minor = 0, int build = 0) { throw null; } public static bool IsMacOS() { throw null; } public static bool IsMacOSVersionAtLeast(int major, int minor = 0, int build = 0) { throw null; } + public static bool IsMacCatalyst() { throw null; } + public static bool IsMacCatalystVersionAtLeast(int major, int minor = 0, int build = 0) { throw null; } public static bool IsTvOS() { throw null; } public static bool IsTvOSVersionAtLeast(int major, int minor = 0, int build = 0) { throw null; } public static bool IsWatchOS() { throw null; } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj index e4b0b3580f5853..dea6068d8881a6 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj @@ -206,6 +206,7 @@ + diff --git a/src/libraries/System.Runtime/tests/System/Reflection/ModuleTests.cs b/src/libraries/System.Runtime/tests/System/Reflection/ModuleTests.cs index 00eda3da5cd1dc..24281af200887b 100644 --- a/src/libraries/System.Runtime/tests/System/Reflection/ModuleTests.cs +++ b/src/libraries/System.Runtime/tests/System/Reflection/ModuleTests.cs @@ -259,7 +259,7 @@ public void ResolveTypeFail(int token) } public static IEnumerable Methods => - Module.GetMethods().Select(m => new object[] { m }); + typeof(ModuleTests).GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly).Select(m => new object[] { m }); [Theory] [MemberData(nameof(Methods))] @@ -288,7 +288,7 @@ public void ResolveMethodFail(int token) } public static IEnumerable Fields => - Module.GetFields().Select(f => new object[] { f }); + typeof(ModuleTests).GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly).Select(f => new object[] { f }); [Theory] [MemberData(nameof(Fields))] diff --git a/src/libraries/System.Runtime/tests/System/Reflection/ReflectionCacheTests.cs b/src/libraries/System.Runtime/tests/System/Reflection/ReflectionCacheTests.cs new file mode 100644 index 00000000000000..f295be7f73faf0 --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/Reflection/ReflectionCacheTests.cs @@ -0,0 +1,43 @@ +// 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.Reflection.Tests +{ + public class ReflectionCacheTests + { + [Fact] + public void GetMethod_MultipleCalls_SameObjects() + { + MethodInfo mi1 = typeof(ReflectionCacheTests).GetMethod(nameof(GetMethod_MultipleCalls_SameObjects)); + Assert.NotNull(mi1); + + MethodInfo mi2 = typeof(ReflectionCacheTests).GetMethod(nameof(GetMethod_MultipleCalls_SameObjects)); + Assert.NotNull(mi2); + + Assert.Same(mi1, mi2); + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/50978", TestRuntimes.Mono)] + [Fact] + public void GetMethod_MultipleCalls_ClearCache_DifferentObjects() + { + Type updateHandler = typeof(Type).Assembly.GetType("System.Reflection.Metadata.RuntimeTypeMetadataUpdateHandler", throwOnError: true, ignoreCase: false); + MethodInfo beforeUpdate = updateHandler.GetMethod("BeforeUpdate", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static, new[] { typeof(Type) }); + Assert.NotNull(beforeUpdate); + + MethodInfo mi1 = typeof(ReflectionCacheTests).GetMethod(nameof(GetMethod_MultipleCalls_ClearCache_DifferentObjects)); + Assert.NotNull(mi1); + Assert.Equal(nameof(GetMethod_MultipleCalls_ClearCache_DifferentObjects), mi1.Name); + + beforeUpdate.Invoke(null, new object[] { typeof(ReflectionCacheTests) }); + + MethodInfo mi2 = typeof(ReflectionCacheTests).GetMethod(nameof(GetMethod_MultipleCalls_ClearCache_DifferentObjects)); + Assert.NotNull(mi2); + Assert.Equal(nameof(GetMethod_MultipleCalls_ClearCache_DifferentObjects), mi2.Name); + + Assert.NotSame(mi1, mi2); + } + } +} diff --git a/src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs b/src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs index ea673dea727b94..2a318e39122ef0 100644 --- a/src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs +++ b/src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs @@ -1839,7 +1839,7 @@ public static void IsDaylightSavingTime_CasablancaMultiYearDaylightSavings(strin } [Fact] - [PlatformSpecific(~TestPlatforms.Windows)] + [SkipOnPlatform(TestPlatforms.Windows, "Not supported on Windows.")] public static void TestSplittingRulesWhenReported() { // This test confirm we are splitting the rules which span multiple years on Linux diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/AesCcmTests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/AesCcmTests.cs index f6ecad13a10ef0..3e60346f40ab67 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/AesCcmTests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/AesCcmTests.cs @@ -8,7 +8,7 @@ namespace System.Security.Cryptography.Algorithms.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public class AesCcmTests : AesAEADTests { [Theory] diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/AesGcmTests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/AesGcmTests.cs index 3cde0f324508e0..70188d08f4238a 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/AesGcmTests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/AesGcmTests.cs @@ -8,7 +8,7 @@ namespace System.Security.Cryptography.Algorithms.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public class AesGcmTests : AesAEADTests { [Theory] diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/AesManagedTests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/AesManagedTests.cs index 681f9ed38367c1..343db25c2e6a07 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/AesManagedTests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/AesManagedTests.cs @@ -11,7 +11,7 @@ namespace System.Security.Cryptography.Encryption.Aes.Tests /// /// Since AesManaged wraps Aes, we only test minimally here. /// - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public class AesManagedTests { [Fact] diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/AesTests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/AesTests.cs index e8c9f02f098d8e..ab562651375901 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/AesTests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/AesTests.cs @@ -5,7 +5,7 @@ namespace System.Security.Cryptography.Algorithms.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public partial class AesTests { [Fact] diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/AsymmetricSignatureFormatterTests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/AsymmetricSignatureFormatterTests.cs index 655735dabe7cec..5704de3fd56d5f 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/AsymmetricSignatureFormatterTests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/AsymmetricSignatureFormatterTests.cs @@ -8,7 +8,7 @@ namespace System.Security.Cryptography.Tests /// /// Helper methods for DSASignatureFormatterTests and RSASignatureFormatterTests /// - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public partial class AsymmetricSignatureFormatterTests { protected static void InvalidFormatterArguments(AsymmetricSignatureFormatter formatter) diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/BlockSizeValueTests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/BlockSizeValueTests.cs index 0faa251e84e383..2f2756ec4c2904 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/BlockSizeValueTests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/BlockSizeValueTests.cs @@ -6,7 +6,7 @@ namespace System.Security.Cryptography.Hashing.Algorithms.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public class BlockSizeValueTests { [Fact] diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/CryptoConfigTests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/CryptoConfigTests.cs index 630aac6d673c29..c84c6fb38c0bcd 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/CryptoConfigTests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/CryptoConfigTests.cs @@ -9,7 +9,7 @@ namespace System.Security.Cryptography.CryptoConfigTests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public static class CryptoConfigTests { [Fact] diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/DSACreateTests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/DSACreateTests.cs index 1f9d231f9d2614..4ced2c4d87ca15 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/DSACreateTests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/DSACreateTests.cs @@ -6,14 +6,14 @@ namespace System.Security.Cryptography.Algorithms.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public static class DSACreateTests { public static bool SupportsKeyGeneration => DSAFactory.SupportsKeyGeneration; public static bool SupportsFips186_3 => DSAFactory.SupportsFips186_3; [ConditionalTheory(nameof(SupportsKeyGeneration))] - [PlatformSpecific(~TestPlatforms.Android)] // Android only supports key sizes that are a multiple of 1024 + [SkipOnPlatform(TestPlatforms.Android, "Android only supports key sizes that are a multiple of 1024")] [InlineData(512)] [InlineData(960)] public static void CreateWithKeysize_SmallKeys(int keySizeInBits) @@ -43,7 +43,7 @@ public static void CreateWithKeysize(int keySizeInBits) } [ConditionalTheory(nameof(SupportsKeyGeneration), nameof(SupportsFips186_3))] - [PlatformSpecific(~TestPlatforms.Android)] // Android only supports key sizes that are a multiple of 1024 + [SkipOnPlatform(TestPlatforms.Android, "Android only supports key sizes that are a multiple of 1024")] [InlineData(1088)] public static void CreateWithKeysize_BigKeys(int keySizeInBits) { diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/DSATests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/DSATests.cs index 57270d6fa200f1..c350e2d1fdcc9d 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/DSATests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/DSATests.cs @@ -8,7 +8,7 @@ namespace System.Security.Cryptography.Algorithms.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public class DSATests { public static bool SupportsKeyGeneration => DSAFactory.SupportsKeyGeneration; diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/ECDiffieHellmanPublicKeyTests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/ECDiffieHellmanPublicKeyTests.cs index d6ef4ab3b29356..81c87279f0e3ba 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/ECDiffieHellmanPublicKeyTests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/ECDiffieHellmanPublicKeyTests.cs @@ -5,7 +5,7 @@ namespace System.Security.Cryptography.EcDiffieHellman.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public class ECDiffieHellmanPublicKeyTests { private class TestDerived : ECDiffieHellmanPublicKey diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/ECDsaTests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/ECDsaTests.cs index b1bcea4272808e..44bed901a6704c 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/ECDsaTests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/ECDsaTests.cs @@ -8,7 +8,7 @@ namespace System.Security.Cryptography.Algorithms.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public class ECDsaTests { [Fact] diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/HKDFTests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/HKDFTests.cs index cf699602df613b..8a15a7b52541a6 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/HKDFTests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/HKDFTests.cs @@ -9,7 +9,7 @@ namespace System.Security.Cryptography.Algorithms.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public abstract class HKDFTests { protected abstract byte[] Extract(HashAlgorithmName hash, int prkLength, byte[] ikm, byte[] salt); diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/HmacMD5Tests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/HmacMD5Tests.cs index 77052ef793f665..c3cc9ea115e0c3 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/HmacMD5Tests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/HmacMD5Tests.cs @@ -6,7 +6,7 @@ namespace System.Security.Cryptography.Hashing.Algorithms.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public class HmacMD5Tests : Rfc2202HmacTests { private static readonly byte[][] s_testKeys2202 = diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/HmacSha1Tests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/HmacSha1Tests.cs index 12542b90bc40fb..4cda1cc1eb2c59 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/HmacSha1Tests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/HmacSha1Tests.cs @@ -6,7 +6,7 @@ namespace System.Security.Cryptography.Hashing.Algorithms.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public class HmacSha1Tests : Rfc2202HmacTests { private static readonly byte[][] s_testKeys2202 = diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/HmacSha256Tests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/HmacSha256Tests.cs index 1024972931ea49..a9d12e55a9c2d6 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/HmacSha256Tests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/HmacSha256Tests.cs @@ -5,7 +5,7 @@ namespace System.Security.Cryptography.Hashing.Algorithms.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public class HmacSha256Tests : Rfc4231HmacTests { protected override HMAC Create() diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/HmacSha384Tests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/HmacSha384Tests.cs index 2bd880c782d714..5a5f0699a2a952 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/HmacSha384Tests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/HmacSha384Tests.cs @@ -5,7 +5,7 @@ namespace System.Security.Cryptography.Hashing.Algorithms.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public class HmacSha384Tests : Rfc4231HmacTests { protected override HMAC Create() diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/HmacSha512Tests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/HmacSha512Tests.cs index 7b5ee6d0e6937c..3136ffd5af0084 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/HmacSha512Tests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/HmacSha512Tests.cs @@ -5,7 +5,7 @@ namespace System.Security.Cryptography.Hashing.Algorithms.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public class HmacSha512Tests : Rfc4231HmacTests { protected override HMAC Create() diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/HmacTests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/HmacTests.cs index 700bf7d3c5fc57..bcabf0c3e69c70 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/HmacTests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/HmacTests.cs @@ -7,7 +7,7 @@ namespace System.Security.Cryptography.Hashing.Algorithms.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public abstract class HmacTests { // RFC2202 defines the test vectors for HMACMD5 and HMACSHA1 diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/IncrementalHashTests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/IncrementalHashTests.cs index b80efd70a8d2e4..2a0f551c279038 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/IncrementalHashTests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/IncrementalHashTests.cs @@ -81,7 +81,7 @@ public static void VerifyIncrementalHash(HashAlgorithm referenceAlgorithm, HashA } [Theory] - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] [MemberData(nameof(GetHMACs))] public static void VerifyIncrementalHMAC(HMAC referenceAlgorithm, HashAlgorithmName hashAlgorithm) { @@ -95,7 +95,7 @@ public static void VerifyIncrementalHMAC(HMAC referenceAlgorithm, HashAlgorithmN } [Theory] - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] [MemberData(nameof(GetHMACs))] public static void VerifyIncrementalHMAC_SpanKey(HMAC referenceAlgorithm, HashAlgorithmName hashAlgorithm) { @@ -162,7 +162,7 @@ public static void VerifyEmptyHash(HashAlgorithm referenceAlgorithm, HashAlgorit } [Theory] - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] [MemberData(nameof(GetHMACs))] public static void VerifyEmptyHMAC(HMAC referenceAlgorithm, HashAlgorithmName hashAlgorithm) { @@ -198,7 +198,7 @@ public static void VerifyTrivialHash(HashAlgorithm referenceAlgorithm, HashAlgor } [Theory] - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] [MemberData(nameof(GetHMACs))] public static void VerifyTrivialHMAC(HMAC referenceAlgorithm, HashAlgorithmName hashAlgorithm) { @@ -229,7 +229,7 @@ public static void AppendDataAfterHashClose() } [Fact] - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public static void AppendDataAfterHMACClose() { using (IncrementalHash hash = IncrementalHash.CreateHMAC(HashAlgorithmName.SHA256, s_hmacKey)) @@ -256,7 +256,7 @@ public static void GetHashTwice() } [Fact] - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public static void GetHMACTwice() { using (IncrementalHash hash = IncrementalHash.CreateHMAC(HashAlgorithmName.SHA256, s_hmacKey)) @@ -280,7 +280,7 @@ public static void ModifyAfterHashDispose() } [Fact] - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public static void ModifyAfterHMACDispose() { using (IncrementalHash hash = IncrementalHash.CreateHMAC(HashAlgorithmName.SHA256, s_hmacKey)) @@ -299,7 +299,7 @@ public static void UnknownDigestAlgorithm() } [Fact] - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public static void UnknownHmacAlgorithm() { Assert.ThrowsAny( @@ -318,7 +318,7 @@ public static void VerifyIncrementalHash_Span(HashAlgorithm referenceAlgorithm, } [Theory] - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] [MemberData(nameof(GetHMACs))] public static void VerifyIncrementalHMAC_Span(HMAC referenceAlgorithm, HashAlgorithmName hashAlgorithm) { @@ -395,7 +395,7 @@ public static void VerifyEmptyHash_Span(HashAlgorithm referenceAlgorithm, HashAl } [Theory] - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] [MemberData(nameof(GetHMACs))] public static void VerifyEmptyHMAC_Span(HMAC referenceAlgorithm, HashAlgorithmName hashAlgorithm) { @@ -433,7 +433,7 @@ public static void VerifyTrivialHash_Span(HashAlgorithm referenceAlgorithm, Hash } [Theory] - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] [MemberData(nameof(GetHMACs))] public static void VerifyTrivialHMAC_Span(HMAC referenceAlgorithm, HashAlgorithmName hashAlgorithm) { @@ -474,7 +474,7 @@ public static void Dispose_HashAlgorithm_ThrowsException(HashAlgorithm reference } [Theory] - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] [MemberData(nameof(GetHMACs))] public static void Dispose_HMAC_ThrowsException(HMAC referenceAlgorithm, HashAlgorithmName hashAlgorithm) { @@ -511,8 +511,8 @@ public static void VerifyGetCurrentHash_Digest(HashAlgorithm referenceAlgorithm, } [Theory] - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] - [PlatformSpecific(~TestPlatforms.Android)] // Android doesn't support cloning the current state for HMAC, so it doesn't support GetCurrentHash + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] + [SkipOnPlatform(TestPlatforms.Android, "Android doesn't support cloning the current state for HMAC, so it doesn't support GetCurrentHash.")] [MemberData(nameof(GetHMACs))] public static void VerifyGetCurrentHash_HMAC(HMAC referenceAlgorithm, HashAlgorithmName hashAlgorithm) { @@ -526,8 +526,8 @@ public static void VerifyGetCurrentHash_HMAC(HMAC referenceAlgorithm, HashAlgori } [Theory] - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] - [PlatformSpecific(~TestPlatforms.Android)] // Android doesn't support cloning the current state for HMAC, so it doesn't support GetCurrentHash + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] + [SkipOnPlatform(TestPlatforms.Android, "Android doesn't support cloning the current state for HMAC, so it doesn't support GetCurrentHash.")] [MemberData(nameof(GetHMACs))] public static void VerifyBounds_GetCurrentHash_HMAC(HMAC referenceAlgorithm, HashAlgorithmName hashAlgorithm) { @@ -548,7 +548,7 @@ public static void VerifyBounds_GetCurrentHash_HMAC(HMAC referenceAlgorithm, Has } [Theory] - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] [MemberData(nameof(GetHMACs))] public static void VerifyBounds_GetHashAndReset_HMAC(HMAC referenceAlgorithm, HashAlgorithmName hashAlgorithm) { diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/InvalidUsageTests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/InvalidUsageTests.cs index 2517780cc96b78..0adfe66cef73e7 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/InvalidUsageTests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/InvalidUsageTests.cs @@ -9,7 +9,7 @@ namespace System.Security.Cryptography.Hashing.Algorithms.Tests public class InvalidUsageTests { [Fact] - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public void InvalidHashCoreArgumentsFromDerivedType() { using (var hmac = new DerivedHMACSHA1()) diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/MD5Tests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/MD5Tests.cs index b1b6cffe28f588..fb84183609dcca 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/MD5Tests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/MD5Tests.cs @@ -5,7 +5,7 @@ namespace System.Security.Cryptography.Hashing.Algorithms.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public class MD5Tests : HashAlgorithmTest { protected override HashAlgorithm Create() diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/PKCS1MaskGenerationMethodTest.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/PKCS1MaskGenerationMethodTest.cs index 77d599113559cc..bc0d9b9a3669a2 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/PKCS1MaskGenerationMethodTest.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/PKCS1MaskGenerationMethodTest.cs @@ -29,7 +29,7 @@ namespace System.Security.Cryptography.Algorithms.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public class PKCS1MaskGenerationMethodTest { [Fact] diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/PaddingModeTests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/PaddingModeTests.cs index d6fcdfbe401a7e..dd82fdba5ad598 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/PaddingModeTests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/PaddingModeTests.cs @@ -10,7 +10,7 @@ namespace System.Security.Cryptography.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public static class PaddingModeTests { [Theory] diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/RSACreateTests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/RSACreateTests.cs index ce0f8dee6661e2..6ccc7e6489c0c7 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/RSACreateTests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/RSACreateTests.cs @@ -6,7 +6,7 @@ namespace System.Security.Cryptography.Algorithms.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public static class RSACreateTests { // macOS has the highest smallest key value, 1024. diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/RSATests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/RSATests.cs index e80c5d139ab18f..0e63e04b0859ad 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/RSATests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/RSATests.cs @@ -7,7 +7,7 @@ namespace System.Security.Cryptography.Algorithms.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public class RSATests { [Fact] diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/ReusabilityTests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/ReusabilityTests.cs index e31a17045ffb47..a9909edd133fcf 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/ReusabilityTests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/ReusabilityTests.cs @@ -6,7 +6,7 @@ namespace System.Security.Cryptography.Hashing.Algorithms.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public class ReusabilityTests { [Theory] diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/Rfc2202HmacTests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/Rfc2202HmacTests.cs index 22f14a276829ee..e66cd896d8bb42 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/Rfc2202HmacTests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/Rfc2202HmacTests.cs @@ -6,7 +6,7 @@ namespace System.Security.Cryptography.Hashing.Algorithms.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public abstract class Rfc2202HmacTests : HmacTests { private static readonly byte[][] s_testData2202 = diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/Rfc2898OneShotTests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/Rfc2898OneShotTests.cs index b0882e7b23486e..eb634e112f240a 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/Rfc2898OneShotTests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/Rfc2898OneShotTests.cs @@ -8,7 +8,7 @@ namespace System.Security.Cryptography.DeriveBytesTests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public static class Rfc2898OneShotTests { // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Suppression approved. Password for testing.")] diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/Rfc2898Tests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/Rfc2898Tests.cs index 497411d6981c47..d733c7535f52e7 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/Rfc2898Tests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/Rfc2898Tests.cs @@ -9,7 +9,7 @@ namespace System.Security.Cryptography.DeriveBytesTests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public class Rfc2898Tests { // 8 bytes is the minimum accepted value, by using it we've already assured that the minimum is acceptable. diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/Rfc4231HmacTests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/Rfc4231HmacTests.cs index ae32e54df0cb65..af8e3e61684477 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/Rfc4231HmacTests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/Rfc4231HmacTests.cs @@ -6,7 +6,7 @@ namespace System.Security.Cryptography.Hashing.Algorithms.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public abstract class Rfc4231HmacTests : HmacTests { private static readonly byte[][] s_testKeys4231 = diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/RijndaelTests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/RijndaelTests.cs index 5fe1c8dee24da5..43243df7335202 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/RijndaelTests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/RijndaelTests.cs @@ -13,7 +13,7 @@ namespace System.Security.Cryptography.Encryption.Rijndael.Tests /// Since RijndaelImplementation (from Rijndael.Create()) and RijndaelManaged classes wrap Aes, /// we only test minimally here. /// - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public class RijndaelTests { [Fact] diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/SignatureDescriptionTests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/SignatureDescriptionTests.cs index 5f1bc43f156301..d26384e2108d47 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/SignatureDescriptionTests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/SignatureDescriptionTests.cs @@ -8,7 +8,7 @@ namespace System.Security.Cryptography.Algorithms.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public class SignatureDescriptionTests { [Fact] diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/TripleDesTests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/TripleDesTests.cs index ebb44ed74fd51f..0a7b2b8e854e1e 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/TripleDesTests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/TripleDesTests.cs @@ -9,7 +9,7 @@ namespace System.Security.Cryptography.Encryption.TripleDes.Tests { - [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public static partial class TripleDesTests { [Fact] diff --git a/src/libraries/System.Security.Cryptography.Csp/tests/System.Security.Cryptography.Csp.Tests.csproj b/src/libraries/System.Security.Cryptography.Csp/tests/System.Security.Cryptography.Csp.Tests.csproj index 5d5e775a415811..6007670ed72358 100644 --- a/src/libraries/System.Security.Cryptography.Csp/tests/System.Security.Cryptography.Csp.Tests.csproj +++ b/src/libraries/System.Security.Cryptography.Csp/tests/System.Security.Cryptography.Csp.Tests.csproj @@ -3,7 +3,6 @@ $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Browser - diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/AssemblyInfo.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/AssemblyInfo.cs deleted file mode 100644 index 3a74f33d7aa32e..00000000000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/AssemblyInfo.cs +++ /dev/null @@ -1,7 +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; -using Xunit; - -[assembly: ActiveIssue("https://github.com/dotnet/runtime/issues/37669", TestPlatforms.Browser)] diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/System.Security.Cryptography.Encoding.Tests.csproj b/src/libraries/System.Security.Cryptography.Encoding/tests/System.Security.Cryptography.Encoding.Tests.csproj index 432a5350c29bcd..945ed4ec62b68d 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/System.Security.Cryptography.Encoding.Tests.csproj +++ b/src/libraries/System.Security.Cryptography.Encoding/tests/System.Security.Cryptography.Encoding.Tests.csproj @@ -4,7 +4,6 @@ $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Browser - diff --git a/src/libraries/System.Security.Cryptography.OpenSsl/tests/AssemblyInfo.cs b/src/libraries/System.Security.Cryptography.OpenSsl/tests/AssemblyInfo.cs deleted file mode 100644 index 3a74f33d7aa32e..00000000000000 --- a/src/libraries/System.Security.Cryptography.OpenSsl/tests/AssemblyInfo.cs +++ /dev/null @@ -1,7 +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; -using Xunit; - -[assembly: ActiveIssue("https://github.com/dotnet/runtime/issues/37669", TestPlatforms.Browser)] diff --git a/src/libraries/System.Security.Cryptography.OpenSsl/tests/System.Security.Cryptography.OpenSsl.Tests.csproj b/src/libraries/System.Security.Cryptography.OpenSsl/tests/System.Security.Cryptography.OpenSsl.Tests.csproj index cb92c2d2c7b07f..e2d4f1aa634fc1 100644 --- a/src/libraries/System.Security.Cryptography.OpenSsl/tests/System.Security.Cryptography.OpenSsl.Tests.csproj +++ b/src/libraries/System.Security.Cryptography.OpenSsl/tests/System.Security.Cryptography.OpenSsl.Tests.csproj @@ -6,7 +6,6 @@ true - - diff --git a/src/libraries/System.Security.Cryptography.Primitives/tests/AssemblyInfo.cs b/src/libraries/System.Security.Cryptography.Primitives/tests/AssemblyInfo.cs deleted file mode 100644 index 3a74f33d7aa32e..00000000000000 --- a/src/libraries/System.Security.Cryptography.Primitives/tests/AssemblyInfo.cs +++ /dev/null @@ -1,7 +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; -using Xunit; - -[assembly: ActiveIssue("https://github.com/dotnet/runtime/issues/37669", TestPlatforms.Browser)] diff --git a/src/libraries/System.Security.Cryptography.Primitives/tests/System.Security.Cryptography.Primitives.Tests.csproj b/src/libraries/System.Security.Cryptography.Primitives/tests/System.Security.Cryptography.Primitives.Tests.csproj index b72afa4f5238e2..ab43c57e984c49 100644 --- a/src/libraries/System.Security.Cryptography.Primitives/tests/System.Security.Cryptography.Primitives.Tests.csproj +++ b/src/libraries/System.Security.Cryptography.Primitives/tests/System.Security.Cryptography.Primitives.Tests.csproj @@ -4,7 +4,6 @@ true - diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs index 5744a52030e7a9..f9968839822700 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs @@ -216,7 +216,7 @@ public static void TestResetMethod() /// [Fact] [OuterLoop] - [PlatformSpecific(~TestPlatforms.Android)] + [SkipOnPlatform(TestPlatforms.Android, "Not supported on Android.")] public static void BuildChainExtraStoreUntrustedRoot() { using (var testCert = new X509Certificate2(TestFiles.ChainPfxFile, TestData.ChainPfxPassword)) @@ -825,7 +825,7 @@ public static void InvalidSelfSignedSignature() [Fact] // Android does not support the detailed status in this test. It always validates time // and trusted root. It will fail to build any chain if those are not valid. - [PlatformSpecific(~TestPlatforms.Android)] + [SkipOnPlatform(TestPlatforms.Android, "Not supported on Android.")] public static void ChainErrorsAtMultipleLayers() { // These certificates were generated for this test using CertificateRequest @@ -910,7 +910,7 @@ public static void ChainErrorsAtMultipleLayers() } [Fact] - [PlatformSpecific(~TestPlatforms.Android)] // Chain building on Android fails with an empty subject + [SkipOnPlatform(TestPlatforms.Android, "Chain building on Android fails with an empty subject")] public static void ChainWithEmptySubject() { using (var cert = new X509Certificate2(TestData.EmptySubjectCertificate)) @@ -1005,7 +1005,7 @@ void CheckChain() } [Fact] - [PlatformSpecific(~TestPlatforms.Linux)] + [SkipOnPlatform(TestPlatforms.Linux, "Not supported on Linux.")] public static void BuildChainForFraudulentCertificate() { // This certificate is a misissued certificate for a "high-value" @@ -1077,7 +1077,7 @@ public static void BuildChainForFraudulentCertificate() } [Fact] - [PlatformSpecific(~TestPlatforms.Linux)] + [SkipOnPlatform(TestPlatforms.Linux, "Not supported on Linux.")] public static void BuildChainForCertificateSignedWithDisallowedKey() { // The intermediate certificate is from the now defunct CA DigiNotar. diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/DynamicChainTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/DynamicChainTests.cs index 223bd49fecbc9c..1b224441a0b092 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/DynamicChainTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/DynamicChainTests.cs @@ -367,7 +367,7 @@ public static void TestLeafCertificateWithUnknownCriticalExtension() } [Fact] - [PlatformSpecific(~TestPlatforms.Android)] // Android does not support AIA fetching + [SkipOnPlatform(TestPlatforms.Android, "Android does not support AIA fetching")] public static void TestInvalidAia() { using (RSA key = RSA.Create()) @@ -413,7 +413,7 @@ public static void TestInvalidAia() // macOS (10.14) will not load certificates with NumericString in their subject // if the 0x12 (NumericString) is changed to 0x13 (PrintableString) then the cert // import doesn't fail. - [PlatformSpecific(~TestPlatforms.OSX)] + [SkipOnPlatform(TestPlatforms.OSX, "Not supported on OSX.")] public static void VerifyNumericStringSubject() { X500DistinguishedName dn = new X500DistinguishedName( @@ -559,7 +559,7 @@ public static void NameConstraintViolation_ExcludedTree_Dns() } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // macOS appears to just completely ignore min/max. + [SkipOnPlatform(TestPlatforms.OSX, "macOS appears to just completely ignore min/max.")] public static void NameConstraintViolation_PermittedTree_HasMin() { SubjectAlternativeNameBuilder builder = new SubjectAlternativeNameBuilder(); @@ -575,9 +575,8 @@ public static void NameConstraintViolation_PermittedTree_HasMin() } [Fact] - // Windows seems to skip over nonsense GeneralNames. - // Android will check for a match. Since the permitted name does match the subject alt name, it succeeds. - [PlatformSpecific(~(TestPlatforms.Windows | TestPlatforms.Android))] + [SkipOnPlatform(TestPlatforms.Windows, "Windows seems to skip over nonsense GeneralNames.")] + [SkipOnPlatform(TestPlatforms.Android, "Android will check for a match. Since the permitted name does match the subject alt name, it succeeds.")] public static void NameConstraintViolation_InvalidGeneralNames() { SubjectAlternativeNameBuilder builder = new SubjectAlternativeNameBuilder(); @@ -646,7 +645,7 @@ public static void MismatchKeyIdentifiers() } [Fact] - [PlatformSpecific(~TestPlatforms.Linux)] + [SkipOnPlatform(TestPlatforms.Linux, "Not supported on Linux.")] public static void PolicyConstraints_RequireExplicitPolicy() { X509Extension[] intermediateExtensions = new [] @@ -703,7 +702,7 @@ public static void PolicyConstraints_Malformed() } [Fact] - [PlatformSpecific(~TestPlatforms.Linux)] + [SkipOnPlatform(TestPlatforms.Linux, "Not supported on Linux.")] public static void PolicyConstraints_Valid() { X509Extension[] intermediateExtensions = new [] @@ -734,7 +733,7 @@ public static void PolicyConstraints_Valid() } [Fact] - [PlatformSpecific(~TestPlatforms.Linux)] + [SkipOnPlatform(TestPlatforms.Linux, "Not supported on Linux.")] public static void PolicyConstraints_Mismatch() { X509Extension[] intermediateExtensions = new [] @@ -769,7 +768,7 @@ public static void PolicyConstraints_Mismatch() } [Fact] - [PlatformSpecific(~TestPlatforms.Linux)] + [SkipOnPlatform(TestPlatforms.Linux, "Not supported on Linux.")] public static void PolicyConstraints_AnyPolicy() { X509Extension[] intermediateExtensions = new [] @@ -800,7 +799,7 @@ public static void PolicyConstraints_AnyPolicy() } [Fact] - [PlatformSpecific(~TestPlatforms.Linux)] + [SkipOnPlatform(TestPlatforms.Linux, "Not supported on Linux.")] public static void PolicyConstraints_Mapped() { X509Extension[] intermediateExtensions = new [] diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/AiaTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/AiaTests.cs index c3729b97b2532c..4dc2a2ae254a06 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/AiaTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/AiaTests.cs @@ -7,7 +7,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests.RevocationTests { - [PlatformSpecific(~TestPlatforms.Android)] // Android does not support AIA fetching + [SkipOnPlatform(TestPlatforms.Android, "Android does not support AIA fetching")] public static class AiaTests { [Fact] diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/DynamicRevocationTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/DynamicRevocationTests.cs index ebf8910855825d..95a26ec5ded866 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/DynamicRevocationTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/DynamicRevocationTests.cs @@ -1134,7 +1134,7 @@ public static void TestRevocationWithNoNextUpdate_Revoked() } [Fact] - [PlatformSpecific(~(TestPlatforms.Android | TestPlatforms.OSX))] // Android and macOS do not support offline revocation chain building. + [SkipOnPlatform(TestPlatforms.Android | TestPlatforms.OSX, "Android and macOS do not support offline revocation chain building.")] public static void TestRevocation_Offline_NotRevoked() { SimpleTest( @@ -1178,7 +1178,7 @@ public static void TestRevocation_Offline_NotRevoked() } [Fact] - [PlatformSpecific(~(TestPlatforms.Android | TestPlatforms.OSX))] // Android and macOS do not support offline revocation chain building. + [SkipOnPlatform(TestPlatforms.Android | TestPlatforms.OSX, "Android and macOS do not support offline revocation chain building.")] public static void TestRevocation_Offline_Revoked() { SimpleTest( diff --git a/src/libraries/System.Security.Cryptography.Xml/tests/AssemblyInfo.cs b/src/libraries/System.Security.Cryptography.Xml/tests/AssemblyInfo.cs deleted file mode 100644 index 3a74f33d7aa32e..00000000000000 --- a/src/libraries/System.Security.Cryptography.Xml/tests/AssemblyInfo.cs +++ /dev/null @@ -1,7 +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; -using Xunit; - -[assembly: ActiveIssue("https://github.com/dotnet/runtime/issues/37669", TestPlatforms.Browser)] diff --git a/src/libraries/System.Security.Cryptography.Xml/tests/System.Security.Cryptography.Xml.Tests.csproj b/src/libraries/System.Security.Cryptography.Xml/tests/System.Security.Cryptography.Xml.Tests.csproj index 2bfad93e7780ff..61ba24acb4c46c 100644 --- a/src/libraries/System.Security.Cryptography.Xml/tests/System.Security.Cryptography.Xml.Tests.csproj +++ b/src/libraries/System.Security.Cryptography.Xml/tests/System.Security.Cryptography.Xml.Tests.csproj @@ -3,7 +3,6 @@ $(NetCoreAppCurrent);net461 - @@ -71,4 +70,4 @@ - \ No newline at end of file + diff --git a/src/libraries/System.Security.Permissions/tests/EvidenceBaseTests.cs b/src/libraries/System.Security.Permissions/tests/EvidenceBaseTests.cs index ba17fea6bb286b..675d9f11a6284e 100644 --- a/src/libraries/System.Security.Permissions/tests/EvidenceBaseTests.cs +++ b/src/libraries/System.Security.Permissions/tests/EvidenceBaseTests.cs @@ -39,7 +39,7 @@ public static void GacInstalledCallMethods() } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] // System.Security.Cryptography.Algorithms is not supported on this platform. + [SkipOnPlatform(TestPlatforms.Browser, "System.Security.Cryptography.Algorithms is not supported on this platform.")] public static void HashCallMethods() { Hash hash = new Hash(Reflection.Assembly.Load(new Reflection.AssemblyName("System.Reflection"))); diff --git a/src/libraries/System.Security.Permissions/tests/MembershipConditionTests.cs b/src/libraries/System.Security.Permissions/tests/MembershipConditionTests.cs index 660661c1520f30..d2714f5a3b6037 100644 --- a/src/libraries/System.Security.Permissions/tests/MembershipConditionTests.cs +++ b/src/libraries/System.Security.Permissions/tests/MembershipConditionTests.cs @@ -61,7 +61,7 @@ public static void GacMembershipConditionCallMethods() } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] // System.Security.Cryptography.Algorithms is not supported on this platform. + [SkipOnPlatform(TestPlatforms.Browser, "System.Security.Cryptography.Algorithms is not supported on this platform.")] public static void HashMembershipConditionCallMethods() { HashMembershipCondition hmc = new HashMembershipCondition(Cryptography.SHA1.Create(), new byte[1]); @@ -79,7 +79,7 @@ public static void HashMembershipConditionCallMethods() } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] // System.Security.Cryptography.X509Certificates is not supported on this platform. + [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()); diff --git a/src/libraries/System.Security.Permissions/tests/PermissionTests.cs b/src/libraries/System.Security.Permissions/tests/PermissionTests.cs index d09b48f343a567..af714344d0dbd2 100644 --- a/src/libraries/System.Security.Permissions/tests/PermissionTests.cs +++ b/src/libraries/System.Security.Permissions/tests/PermissionTests.cs @@ -210,7 +210,7 @@ public static void PrincipalPermissionCallMethods() } [Fact] - [PlatformSpecific(~TestPlatforms.Browser)] // System.Security.Cryptography.X509Certificates is not supported on this platform. + [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()); diff --git a/src/libraries/System.Text.Encoding/tests/Encoding/TranscodingStreamTests.cs b/src/libraries/System.Text.Encoding/tests/Encoding/TranscodingStreamTests.cs index c93779302a0bf0..ffb9f86903c574 100644 --- a/src/libraries/System.Text.Encoding/tests/Encoding/TranscodingStreamTests.cs +++ b/src/libraries/System.Text.Encoding/tests/Encoding/TranscodingStreamTests.cs @@ -1,9 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.IO.Pipelines; using System.IO.Tests; using System.Threading; using System.Threading.Tasks; @@ -336,10 +338,10 @@ private void RunReadTest(Func callback) Assert.Equal(-1, transcodingStream.ReadByte()); // should've reached EOF - // Now put some invalid data into the inner stream as EOF. + // Now put some invalid data into the inner stream, followed by EOF, and ensure we get U+FFFD back out. innerStream.SetLength(0); // reset - innerStream.WriteByte(0xC0); + innerStream.WriteByte(0xC0); // [ C0 ] is never valid in UTF-8 innerStream.Position = 0; sink.SetLength(0); // reset @@ -353,6 +355,22 @@ private void RunReadTest(Func callback) Assert.Equal("[FFFD]", ErrorCheckingAsciiEncoding.GetString(sink.ToArray())); Assert.Equal(-1, transcodingStream.ReadByte()); // should've reached EOF + // Now put some incomplete data into the inner stream, followed by EOF, and ensure we get U+FFFD back out. + + innerStream.SetLength(0); // reset + innerStream.WriteByte(0xC2); // [ C2 ] must be followed by [ 80..BF ] in UTF-8 + innerStream.Position = 0; + + sink.SetLength(0); // reset + do + { + numBytesReadJustNow = callback(transcodingStream, sink); + Assert.True(numBytesReadJustNow >= 0); + } while (numBytesReadJustNow > 0); + + Assert.Equal("[FFFD]", ErrorCheckingAsciiEncoding.GetString(sink.ToArray())); + Assert.Equal(-1, transcodingStream.ReadByte()); // should've reached EOF + void RunOneTestIteration(int stringLength) { sink.SetLength(0); // reset @@ -452,6 +470,56 @@ await RunReadTestAsync(async (transcodingStream, cancellationToken, sink) => }); } + [Fact] + public async Task ReadAsync_LoopsWhenPartialDataReceived() + { + // Validates that the TranscodingStream will loop instead of returning 0 + // if the inner stream read partial data and GetBytes cannot make forward progress. + + using AsyncComms comms = new AsyncComms(); + Stream transcodingStream = Encoding.CreateTranscodingStream(comms.ReadStream, Encoding.UTF8, Encoding.UTF8); + + // First, ensure that writing [ C0 ] (always invalid UTF-8) to the stream + // causes the reader to return immediately with fallback behavior. + + byte[] readBuffer = new byte[1024]; + comms.WriteBytes(new byte[] { 0xC0 }); + + int numBytesRead = await transcodingStream.ReadAsync(readBuffer.AsMemory()); + Assert.Equal(new byte[] { 0xEF, 0xBF, 0xBD }, readBuffer[0..numBytesRead]); // fallback substitution + + // Next, ensure that writing [ C2 ] (partial UTF-8, needs more data) to the stream + // causes the reader to asynchronously loop, returning "not yet complete". + + readBuffer = new byte[1024]; + comms.WriteBytes(new byte[] { 0xC2 }); + + ValueTask task = transcodingStream.ReadAsync(readBuffer.AsMemory()); + Assert.False(task.IsCompleted); + comms.WriteBytes(new byte[] { 0x80 }); // [ C2 80 ] is valid UTF-8 + + numBytesRead = await task; // should complete successfully + Assert.Equal(new byte[] { 0xC2, 0x80 }, readBuffer[0..numBytesRead]); + + // Finally, ensure that writing [ C2 ] (partial UTF-8, needs more data) to the stream + // followed by EOF causes the reader to perform substitution before returning EOF. + + readBuffer = new byte[1024]; + comms.WriteBytes(new byte[] { 0xC2 }); + + task = transcodingStream.ReadAsync(readBuffer.AsMemory()); + Assert.False(task.IsCompleted); + comms.WriteEof(); + + numBytesRead = await task; // should complete successfully + Assert.Equal(new byte[] { 0xEF, 0xBF, 0xBD }, readBuffer[0..numBytesRead]); // fallback substitution + + // Next call really should return "EOF reached" + + readBuffer = new byte[1024]; + Assert.Equal(0, await transcodingStream.ReadAsync(readBuffer.AsMemory())); + } + [Fact] public void ReadAsync_WithInvalidArgs_Throws() { @@ -510,10 +578,10 @@ private async Task RunReadTestAsync(Func= 0); + } while (numBytesReadJustNow > 0); + + Assert.Equal("[FFFD]", ErrorCheckingAsciiEncoding.GetString(sink.ToArray())); + Assert.Equal(-1, await transcodingStream.ReadByteAsync(expectedCancellationToken)); // should've reached EOF + async Task RunOneTestIteration(int stringLength) { sink.SetLength(0); // reset @@ -942,5 +1026,49 @@ public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] public override byte[] GetPreamble() => Array.Empty(); } + + // A helper type that allows synchronously writing to a stream while asynchronously + // reading from it. + private sealed class AsyncComms : IDisposable + { + private readonly BlockingCollection _blockingCollection; + private readonly PipeWriter _writer; + + public AsyncComms() + { + _blockingCollection = new BlockingCollection(); + var pipe = new Pipe(); + ReadStream = pipe.Reader.AsStream(); + _writer = pipe.Writer; + Task.Run(_DrainWorker); + } + + public Stream ReadStream { get; } + + public void Dispose() + { + _blockingCollection.Dispose(); + } + + public void WriteBytes(ReadOnlySpan bytes) + { + _blockingCollection.Add(bytes.ToArray()); + } + + public void WriteEof() + { + _blockingCollection.Add(null); + } + + private async Task _DrainWorker() + { + byte[] buffer; + while ((buffer = _blockingCollection.Take()) is not null) + { + await _writer.WriteAsync(buffer); + } + _writer.Complete(); + } + } } } diff --git a/src/libraries/System.Text.Encoding/tests/System.Text.Encoding.Tests.csproj b/src/libraries/System.Text.Encoding/tests/System.Text.Encoding.Tests.csproj index 9015b7c7069597..dd0551c8c467e3 100644 --- a/src/libraries/System.Text.Encoding/tests/System.Text.Encoding.Tests.csproj +++ b/src/libraries/System.Text.Encoding/tests/System.Text.Encoding.Tests.csproj @@ -93,5 +93,6 @@ + \ No newline at end of file diff --git a/src/libraries/System.Text.Json/ref/System.Text.Json.cs b/src/libraries/System.Text.Json/ref/System.Text.Json.cs index cf0f709ad2d2c5..48a4c8c5f9e519 100644 --- a/src/libraries/System.Text.Json/ref/System.Text.Json.cs +++ b/src/libraries/System.Text.Json/ref/System.Text.Json.cs @@ -192,6 +192,7 @@ public static partial class JsonSerializer public static object? Deserialize(ref System.Text.Json.Utf8JsonReader reader, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] System.Type returnType, System.Text.Json.JsonSerializerOptions? options = null) { throw null; } public static System.Threading.Tasks.ValueTask DeserializeAsync(System.IO.Stream utf8Json, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] System.Type returnType, System.Text.Json.JsonSerializerOptions? options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public static System.Threading.Tasks.ValueTask DeserializeAsync<[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] TValue>(System.IO.Stream utf8Json, System.Text.Json.JsonSerializerOptions? options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public static System.Collections.Generic.IAsyncEnumerable DeserializeAsyncEnumerable<[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] TValue>(System.IO.Stream utf8Json, System.Text.Json.JsonSerializerOptions? options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public static TValue? Deserialize<[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] TValue>(System.ReadOnlySpan utf8Json, System.Text.Json.JsonSerializerOptions? options = null) { throw null; } public static TValue? Deserialize<[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] TValue>(string json, System.Text.Json.JsonSerializerOptions? options = null) { throw null; } public static TValue? Deserialize<[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] TValue>(System.ReadOnlySpan json, System.Text.Json.JsonSerializerOptions? options = null) { throw null; } diff --git a/src/libraries/System.Text.Json/src/Resources/Strings.resx b/src/libraries/System.Text.Json/src/Resources/Strings.resx index c6039fdeba147f..47c651107dfa33 100644 --- a/src/libraries/System.Text.Json/src/Resources/Strings.resx +++ b/src/libraries/System.Text.Json/src/Resources/Strings.resx @@ -357,6 +357,9 @@ The type '{0}' is not supported. + + The type '{0}' can only be serialized using async serialization methods. + '{0}' is invalid after '/' at the beginning of the comment. Expected either '/' or '*'. @@ -557,4 +560,4 @@ The converter '{0}' cannot return an instance of JsonConverterFactory. - \ No newline at end of file + diff --git a/src/libraries/System.Text.Json/src/System.Text.Json.csproj b/src/libraries/System.Text.Json/src/System.Text.Json.csproj index cb91cbdfdb4efb..c84780fae369a6 100644 --- a/src/libraries/System.Text.Json/src/System.Text.Json.csproj +++ b/src/libraries/System.Text.Json/src/System.Text.Json.csproj @@ -73,6 +73,8 @@ + + @@ -173,6 +175,7 @@ + @@ -233,11 +236,9 @@ - + - + diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ClassType.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ClassType.cs index 7dd4fe798d1315..aedc4343192f53 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ClassType.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ClassType.cs @@ -20,7 +20,7 @@ internal enum ClassType : byte Value = 0x2, // JsonValueConverter<> - simple values that need to re-enter the serializer such as KeyValuePair. NewValue = 0x4, - // JsonIEnumerbleConverter<> - all enumerable collections except dictionaries. + // JsonIEnumerableConverter<> - all enumerable collections except dictionaries. Enumerable = 0x8, // JsonDictionaryConverter<,> - dictionary types. Dictionary = 0x10, diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IAsyncEnumerableConverterFactory.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IAsyncEnumerableConverterFactory.cs new file mode 100644 index 00000000000000..d9ad1615bb4c28 --- /dev/null +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IAsyncEnumerableConverterFactory.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization.Converters; + +namespace System.Text.Json.Serialization +{ + /// + /// Converter for streaming values. + /// + internal sealed class IAsyncEnumerableConverterFactory : JsonConverterFactory + { + public override bool CanConvert(Type typeToConvert) => GetAsyncEnumerableInterface(typeToConvert) is not null; + + public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) + { + Type? asyncEnumerableInterface = GetAsyncEnumerableInterface(typeToConvert); + Debug.Assert(asyncEnumerableInterface is not null, $"{typeToConvert} not supported by converter."); + + Type elementType = asyncEnumerableInterface.GetGenericArguments()[0]; + Type converterType = typeof(IAsyncEnumerableOfTConverter<,>).MakeGenericType(typeToConvert, elementType); + return (JsonConverter)Activator.CreateInstance(converterType)!; + } + + private static Type? GetAsyncEnumerableInterface(Type type) + => IEnumerableConverterFactoryHelpers.GetCompatibleGenericInterface(type, typeof(IAsyncEnumerable<>)); + } +} diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IAsyncEnumerableOfTConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IAsyncEnumerableOfTConverter.cs new file mode 100644 index 00000000000000..3deef03e10fbb4 --- /dev/null +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IAsyncEnumerableOfTConverter.cs @@ -0,0 +1,126 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; + +namespace System.Text.Json.Serialization.Converters +{ + internal sealed class IAsyncEnumerableOfTConverter + : IEnumerableDefaultConverter + where TAsyncEnumerable : IAsyncEnumerable + { + internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, ref ReadStack state, out TAsyncEnumerable value) + { + if (!typeToConvert.IsAssignableFrom(typeof(IAsyncEnumerable))) + { + ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state); + } + + return base.OnTryRead(ref reader, typeToConvert, options, ref state, out value!); + } + + protected override void Add(in TElement value, ref ReadStack state) + { + ((BufferedAsyncEnumerable)state.Current.ReturnValue!)._buffer.Add(value); + } + + protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) + { + state.Current.ReturnValue = new BufferedAsyncEnumerable(); + } + + internal override bool OnTryWrite(Utf8JsonWriter writer, TAsyncEnumerable value, JsonSerializerOptions options, ref WriteStack state) + { + if (!state.SupportContinuation) + { + ThrowHelper.ThrowNotSupportedException_TypeRequiresAsyncSerialization(TypeToConvert); + } + + return base.OnTryWrite(writer, value, options, ref state); + } + + [Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2012:Use ValueTasks correctly", Justification = "Converter needs to consume ValueTask's in a non-async context")] + protected override bool OnWriteResume(Utf8JsonWriter writer, TAsyncEnumerable value, JsonSerializerOptions options, ref WriteStack state) + { + IAsyncEnumerator enumerator; + ValueTask moveNextTask; + + if (state.Current.AsyncEnumerator is null) + { + enumerator = value.GetAsyncEnumerator(state.CancellationToken); + moveNextTask = enumerator.MoveNextAsync(); + // we always need to attach the enumerator to the stack + // since it will need to be disposed asynchronously. + state.Current.AsyncEnumerator = enumerator; + } + else + { + Debug.Assert(state.Current.AsyncEnumerator is IAsyncEnumerator); + enumerator = (IAsyncEnumerator)state.Current.AsyncEnumerator; + + if (state.Current.AsyncEnumeratorIsPendingCompletion) + { + // converter was previously suspended due to a pending MoveNextAsync() task + Debug.Assert(state.PendingTask is Task && state.PendingTask.IsCompleted); + moveNextTask = new ValueTask((Task)state.PendingTask); + state.Current.AsyncEnumeratorIsPendingCompletion = false; + state.PendingTask = null; + } + else + { + // converter was suspended for a different reason; + // the last MoveNextAsync() call can only have completed with 'true'. + moveNextTask = new ValueTask(true); + } + } + + JsonConverter converter = GetElementConverter(ref state); + + // iterate through the enumerator while elements are being returned synchronously + for (; moveNextTask.IsCompleted; moveNextTask = enumerator.MoveNextAsync()) + { + if (!moveNextTask.Result) + { + return true; + } + + if (ShouldFlush(writer, ref state)) + { + return false; + } + + TElement element = enumerator.Current; + if (!converter.TryWrite(writer, element, options, ref state)) + { + return false; + } + } + + // we have a pending MoveNextAsync() call; + // wrap inside a regular task so that it can be awaited multiple times; + // mark the current stackframe as pending completion. + Debug.Assert(state.PendingTask is null); + state.PendingTask = moveNextTask.AsTask(); + state.Current.AsyncEnumeratorIsPendingCompletion = true; + return false; + } + + private sealed class BufferedAsyncEnumerable : IAsyncEnumerable + { + public readonly List _buffer = new(); + +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + public async IAsyncEnumerator GetAsyncEnumerator(CancellationToken _) + { + foreach (TElement element in _buffer) + { + yield return element; + } + } +#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously + } + } +} diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableDefaultConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableDefaultConverter.cs index 67613d90973a30..0400b617121731 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableDefaultConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableDefaultConverter.cs @@ -218,7 +218,7 @@ internal override bool OnTryRead( return true; } - internal sealed override bool OnTryWrite( + internal override bool OnTryWrite( Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonResumableConverterOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonResumableConverterOfT.cs index 05efb460b64f1b..e1d7eb14c6b0f8 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonResumableConverterOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonResumableConverterOfT.cs @@ -36,7 +36,15 @@ public sealed override void Write(Utf8JsonWriter writer, T value, JsonSerializer WriteStack state = default; state.Initialize(typeof(T), options, supportContinuation: false); - TryWrite(writer, value, options, ref state); + try + { + TryWrite(writer, value, options, ref state); + } + catch + { + state.DisposePendingDisposablesOnException(); + throw; + } } public sealed override bool HandleNull => false; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Stream.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Stream.cs index 5c695a62c43f27..5e4b09d6b3fa8a 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Stream.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Stream.cs @@ -2,9 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers; +using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Runtime.CompilerServices; using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; @@ -45,7 +47,7 @@ public static partial class JsonSerializer throw new ArgumentNullException(nameof(utf8Json)); } - return ReadAsync(utf8Json, typeof(TValue), options, cancellationToken); + return ReadAllAsync(utf8Json, typeof(TValue), options, cancellationToken); } /// @@ -83,137 +85,207 @@ public static partial class JsonSerializer if (returnType == null) throw new ArgumentNullException(nameof(returnType)); - return ReadAsync(utf8Json, returnType, options, cancellationToken); + return ReadAllAsync(utf8Json, returnType, options, cancellationToken); } - private static async ValueTask ReadAsync( + /// + /// Wraps the UTF-8 encoded text into an + /// that can be used to deserialize root-level JSON arrays in a streaming manner. + /// + /// An representation of the provided JSON array. + /// JSON data to parse. + /// Options to control the behavior during reading. + /// The which may be used to cancel the read operation. + /// An representation of the JSON value. + /// + /// is . + /// + public static IAsyncEnumerable DeserializeAsyncEnumerable<[DynamicallyAccessedMembers(MembersAccessedOnRead)] TValue>( Stream utf8Json, - Type returnType, - JsonSerializerOptions? options, - CancellationToken cancellationToken) + JsonSerializerOptions? options = null, + CancellationToken cancellationToken = default) { - if (options == null) + if (utf8Json == null) { - options = JsonSerializerOptions.s_defaultOptions; + throw new ArgumentNullException(nameof(utf8Json)); } - ReadStack state = default; - state.Initialize(returnType, options, supportContinuation: true); + options ??= JsonSerializerOptions.s_defaultOptions; + return CreateAsyncEnumerableDeserializer(utf8Json, options, cancellationToken); - JsonConverter converter = state.Current.JsonPropertyInfo!.ConverterBase; + static async IAsyncEnumerable CreateAsyncEnumerableDeserializer( + Stream utf8Json, + JsonSerializerOptions options, + [EnumeratorCancellation] CancellationToken cancellationToken) + { + var bufferState = new ReadAsyncBufferState(options.DefaultBufferSize); + ReadStack readStack = default; + readStack.Initialize(typeof(Queue), options, supportContinuation: true); + JsonConverter converter = readStack.Current.JsonPropertyInfo!.ConverterBase; + var jsonReaderState = new JsonReaderState(options.GetReaderOptions()); - var readerState = new JsonReaderState(options.GetReaderOptions()); + try + { + do + { + bufferState = await ReadFromStreamAsync(utf8Json, bufferState, cancellationToken).ConfigureAwait(false); + ContinueDeserialize>(ref bufferState, ref jsonReaderState, ref readStack, converter, options); + if (readStack.Current.ReturnValue is Queue queue) + { + while (queue.Count > 0) + { + yield return queue.Dequeue(); + } + } + } + while (!bufferState.IsFinalBlock); + } + finally + { + bufferState.Dispose(); + } + } + } - // todo: https://github.com/dotnet/runtime/issues/32355 - int utf8BomLength = JsonConstants.Utf8Bom.Length; - byte[] buffer = ArrayPool.Shared.Rent(Math.Max(options.DefaultBufferSize, utf8BomLength)); - int bytesInBuffer = 0; - long totalBytesRead = 0; - int clearMax = 0; - bool isFirstIteration = true; + internal static async ValueTask ReadAllAsync( + Stream utf8Json, + Type inputType, + JsonSerializerOptions? options, + CancellationToken cancellationToken) + { + options ??= JsonSerializerOptions.s_defaultOptions; + var asyncState = new ReadAsyncBufferState(options.DefaultBufferSize); + ReadStack readStack = default; + readStack.Initialize(inputType, options, supportContinuation: true); + JsonConverter converter = readStack.Current.JsonPropertyInfo!.ConverterBase; + var jsonReaderState = new JsonReaderState(options.GetReaderOptions()); try { while (true) { - // Read from the stream until either our buffer is filled or we hit EOF. - // Calling ReadCore is relatively expensive, so we minimize the number of times - // we need to call it. - bool isFinalBlock = false; - while (true) + asyncState = await ReadFromStreamAsync(utf8Json, asyncState, cancellationToken).ConfigureAwait(false); + TValue value = ContinueDeserialize(ref asyncState, ref jsonReaderState, ref readStack, converter, options); + + if (asyncState.IsFinalBlock) { - int bytesRead = await utf8Json.ReadAsync( + return value!; + } + } + } + finally + { + asyncState.Dispose(); + } + } + + /// + /// Read from the stream until either our buffer is filled or we hit EOF. + /// Calling ReadCore is relatively expensive, so we minimize the number of times + /// we need to call it. + /// + internal static async ValueTask ReadFromStreamAsync( + Stream utf8Json, + ReadAsyncBufferState bufferState, + CancellationToken cancellationToken) + { + while (true) + { + int bytesRead = await utf8Json.ReadAsync( #if BUILDING_INBOX_LIBRARY - buffer.AsMemory(bytesInBuffer), + bufferState.Buffer.AsMemory(bufferState.BytesInBuffer), #else - buffer, bytesInBuffer, buffer.Length - bytesInBuffer, + bufferState.Buffer, bufferState.BytesInBuffer, bufferState.Buffer.Length - bufferState.BytesInBuffer, #endif - cancellationToken).ConfigureAwait(false); + cancellationToken).ConfigureAwait(false); - if (bytesRead == 0) - { - isFinalBlock = true; - break; - } + if (bytesRead == 0) + { + bufferState.IsFinalBlock = true; + break; + } - totalBytesRead += bytesRead; - bytesInBuffer += bytesRead; + bufferState.BytesInBuffer += bytesRead; - if (bytesInBuffer == buffer.Length) - { - break; - } - } - - if (bytesInBuffer > clearMax) - { - clearMax = bytesInBuffer; - } + if (bufferState.BytesInBuffer == bufferState.Buffer.Length) + { + break; + } + } - int start = 0; - if (isFirstIteration) - { - isFirstIteration = false; + return bufferState; + } - // Handle the UTF-8 BOM if present - Debug.Assert(buffer.Length >= JsonConstants.Utf8Bom.Length); - if (buffer.AsSpan().StartsWith(JsonConstants.Utf8Bom)) - { - start += utf8BomLength; - bytesInBuffer -= utf8BomLength; - } - } + internal static TValue ContinueDeserialize( + ref ReadAsyncBufferState bufferState, + ref JsonReaderState jsonReaderState, + ref ReadStack readStack, + JsonConverter converter, + JsonSerializerOptions options) + { + if (bufferState.BytesInBuffer > bufferState.ClearMax) + { + bufferState.ClearMax = bufferState.BytesInBuffer; + } - // Process the data available - TValue value = ReadCore( - ref readerState, - isFinalBlock, - new ReadOnlySpan(buffer, start, bytesInBuffer), - options, - ref state, - converter); + int start = 0; + if (bufferState.IsFirstIteration) + { + bufferState.IsFirstIteration = false; - Debug.Assert(state.BytesConsumed <= bytesInBuffer); - int bytesConsumed = checked((int)state.BytesConsumed); + // Handle the UTF-8 BOM if present + Debug.Assert(bufferState.Buffer.Length >= JsonConstants.Utf8Bom.Length); + if (bufferState.Buffer.AsSpan().StartsWith(JsonConstants.Utf8Bom)) + { + start += JsonConstants.Utf8Bom.Length; + bufferState.BytesInBuffer -= JsonConstants.Utf8Bom.Length; + } + } - bytesInBuffer -= bytesConsumed; + // Process the data available + TValue value = ReadCore( + ref jsonReaderState, + bufferState.IsFinalBlock, + new ReadOnlySpan(bufferState.Buffer, start, bufferState.BytesInBuffer), + options, + ref readStack, + converter); - if (isFinalBlock) - { - // The reader should have thrown if we have remaining bytes. - Debug.Assert(bytesInBuffer == 0); + Debug.Assert(readStack.BytesConsumed <= bufferState.BytesInBuffer); + int bytesConsumed = checked((int)readStack.BytesConsumed); - return value; - } + bufferState.BytesInBuffer -= bytesConsumed; - // Check if we need to shift or expand the buffer because there wasn't enough data to complete deserialization. - if ((uint)bytesInBuffer > ((uint)buffer.Length / 2)) - { - // We have less than half the buffer available, double the buffer size. - byte[] dest = ArrayPool.Shared.Rent((buffer.Length < (int.MaxValue / 2)) ? buffer.Length * 2 : int.MaxValue); + // The reader should have thrown if we have remaining bytes. + Debug.Assert(!bufferState.IsFinalBlock || bufferState.BytesInBuffer == 0); - // Copy the unprocessed data to the new buffer while shifting the processed bytes. - Buffer.BlockCopy(buffer, bytesConsumed + start, dest, 0, bytesInBuffer); + if (!bufferState.IsFinalBlock) + { + // Check if we need to shift or expand the buffer because there wasn't enough data to complete deserialization. + if ((uint)bufferState.BytesInBuffer > ((uint)bufferState.Buffer.Length / 2)) + { + // We have less than half the buffer available, double the buffer size. + byte[] oldBuffer = bufferState.Buffer; + int oldClearMax = bufferState.ClearMax; + byte[] newBuffer = ArrayPool.Shared.Rent((bufferState.Buffer.Length < (int.MaxValue / 2)) ? bufferState.Buffer.Length * 2 : int.MaxValue); - new Span(buffer, 0, clearMax).Clear(); - ArrayPool.Shared.Return(buffer); + // Copy the unprocessed data to the new buffer while shifting the processed bytes. + Buffer.BlockCopy(oldBuffer, bytesConsumed + start, newBuffer, 0, bufferState.BytesInBuffer); + bufferState.Buffer = newBuffer; + bufferState.ClearMax = bufferState.BytesInBuffer; - clearMax = bytesInBuffer; - buffer = dest; - } - else if (bytesInBuffer != 0) - { - // Shift the processed bytes to the beginning of buffer to make more room. - Buffer.BlockCopy(buffer, bytesConsumed + start, buffer, 0, bytesInBuffer); - } + // Clear and return the old buffer + new Span(oldBuffer, 0, oldClearMax).Clear(); + ArrayPool.Shared.Return(oldBuffer); + } + else if (bufferState.BytesInBuffer != 0) + { + // Shift the processed bytes to the beginning of buffer to make more room. + Buffer.BlockCopy(bufferState.Buffer, bytesConsumed + start, bufferState.Buffer, 0, bufferState.BytesInBuffer); } } - finally - { - // Clear only what we used and return the buffer to the pool - new Span(buffer, 0, clearMax).Clear(); - ArrayPool.Shared.Return(buffer); - } + + return value; } private static TValue ReadCore( @@ -234,7 +306,6 @@ private static TValue ReadCore( state.BytesConsumed = 0; TValue? value = ReadCore(converterBase, ref reader, options, ref state); - readerState = reader.CurrentState; return value!; } 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 6f9dea9343c6e1..75aa2de65f0848 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 @@ -29,8 +29,16 @@ private static void WriteCore( WriteStack state = default; JsonConverter jsonConverter = state.Initialize(inputType, options, supportContinuation: false); - bool success = WriteCore(jsonConverter, writer, value, options, ref state); - Debug.Assert(success); + try + { + bool success = WriteCore(jsonConverter, writer, value, options, ref state); + Debug.Assert(success); + } + catch + { + state.DisposePendingDisposablesOnException(); + throw; + } } private static bool WriteCore( diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Stream.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Stream.cs index 6e4a3ef256442b..015b136247c302 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Stream.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Stream.cs @@ -113,21 +113,52 @@ private static async Task WriteAsyncCore( inputType = value!.GetType(); } - WriteStack state = default; + WriteStack state = new WriteStack { CancellationToken = cancellationToken }; JsonConverter converterBase = state.Initialize(inputType, options, supportContinuation: true); bool isFinalBlock; - do + try { - state.FlushThreshold = (int)(bufferWriter.Capacity * FlushThreshold); - - isFinalBlock = WriteCore(converterBase, writer, value, options, ref state); - - await bufferWriter.WriteToStreamAsync(utf8Json, cancellationToken).ConfigureAwait(false); - - bufferWriter.Clear(); - } while (!isFinalBlock); + do + { + state.FlushThreshold = (int)(bufferWriter.Capacity * FlushThreshold); + + try + { + isFinalBlock = WriteCore(converterBase, writer, value, options, ref state); + } + finally + { + if (state.PendingAsyncDisposables?.Count > 0) + { + await state.DisposePendingAsyncDisposables().ConfigureAwait(false); + } + } + + await bufferWriter.WriteToStreamAsync(utf8Json, cancellationToken).ConfigureAwait(false); + bufferWriter.Clear(); + + if (state.PendingTask is not null) + { + try + { + await state.PendingTask.ConfigureAwait(false); + } + catch + { + // Exceptions will be propagated elsewhere + // TODO https://github.com/dotnet/runtime/issues/22144 + } + } + + } while (!isFinalBlock); + } + catch + { + await state.DisposePendingDisposablesOnExceptionAsync().ConfigureAwait(false); + throw; + } } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.Converters.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.Converters.cs index 2f17deede6d5b1..226f86f171a560 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.Converters.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.Converters.cs @@ -26,6 +26,8 @@ public sealed partial class JsonSerializerOptions // Nullable converter should always be next since it forwards to any nullable type. new NullableConverterFactory(), new EnumConverterFactory(), + // IAsyncEnumerable takes precedence over IEnumerable. + new IAsyncEnumerableConverterFactory(), // IEnumerable should always be second to last since they can convert any IEnumerable. new IEnumerableConverterFactory(), // Object should always be last since it converts any type. diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadAsyncBufferState.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadAsyncBufferState.cs new file mode 100644 index 00000000000000..2fac132521fed8 --- /dev/null +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadAsyncBufferState.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.Diagnostics; +using System.Threading; + +namespace System.Text.Json.Serialization +{ + internal struct ReadAsyncBufferState : IDisposable + { + public byte[] Buffer; + public int BytesInBuffer; + public int ClearMax; + public bool IsFirstIteration; + public bool IsFinalBlock; + + public ReadAsyncBufferState(int defaultBufferSize) + { + Buffer = ArrayPool.Shared.Rent(Math.Max(defaultBufferSize, JsonConstants.Utf8Bom.Length)); + BytesInBuffer = ClearMax = 0; + IsFirstIteration = true; + IsFinalBlock = false; + } + + public void Dispose() + { + // Clear only what we used and return the buffer to the pool + new Span(Buffer, 0, ClearMax).Clear(); + ArrayPool.Shared.Return(Buffer); + Buffer = null!; + } + } +} 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 7f231046fc036d..99b2ce37e3f3c2 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 @@ -1,9 +1,13 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections; using System.Collections.Generic; using System.Diagnostics; +using System.Runtime.ExceptionServices; using System.Text.Json.Serialization; +using System.Threading; +using System.Threading.Tasks; namespace System.Text.Json { @@ -20,6 +24,22 @@ internal struct WriteStack /// private int _count; + /// + /// Cancellation token used by converters performing async serialization (e.g. IAsyncEnumerable) + /// + public CancellationToken CancellationToken; + + /// + /// Stores a pending task that a resumable converter depends on to continue work. + /// It must be awaited by the root context before serialization is resumed. + /// + public Task? PendingTask; + + /// + /// List of IAsyncDisposables that have been scheduled for disposal by converters. + /// + public List? PendingAsyncDisposables; + private List _previous; // A field is used instead of a property to avoid value semantics. @@ -172,6 +192,14 @@ public void Pop(bool success) else { Debug.Assert(_continuationCount == 0); + + if (Current.AsyncEnumerator is not null) + { + // we have completed serialization of an AsyncEnumerator, + // pop from the stack and schedule for async disposal. + PendingAsyncDisposables ??= new List(); + PendingAsyncDisposables.Add(Current.AsyncEnumerator); + } } if (_count > 1) @@ -180,6 +208,122 @@ public void Pop(bool success) } } + // Asynchronously dispose of any AsyncDisposables that have been scheduled for disposal + public async ValueTask DisposePendingAsyncDisposables() + { + Debug.Assert(PendingAsyncDisposables?.Count > 0); + Exception? exception = null; + + foreach (IAsyncDisposable asyncDisposable in PendingAsyncDisposables) + { + try + { + await asyncDisposable.DisposeAsync().ConfigureAwait(false); + } + catch (Exception e) + { + exception = e; + } + } + + if (exception is not null) + { + ExceptionDispatchInfo.Capture(exception).Throw(); + } + + PendingAsyncDisposables.Clear(); + } + + /// + /// Walks the stack cleaning up any leftover IDisposables + /// in the event of an exception on serialization + /// + public void DisposePendingDisposablesOnException() + { + Exception? exception = null; + + Debug.Assert(Current.AsyncEnumerator is null); + DisposeFrame(Current.CollectionEnumerator, ref exception); + + int stackSize = Math.Max(_count, _continuationCount); + if (stackSize > 1) + { + for (int i = 0; i < stackSize - 1; i++) + { + Debug.Assert(_previous[i].AsyncEnumerator is null); + DisposeFrame(_previous[i].CollectionEnumerator, ref exception); + } + } + + if (exception is not null) + { + ExceptionDispatchInfo.Capture(exception).Throw(); + } + + static void DisposeFrame(IEnumerator? collectionEnumerator, ref Exception? exception) + { + try + { + if (collectionEnumerator is IDisposable disposable) + { + disposable.Dispose(); + } + } + catch (Exception e) + { + exception = e; + } + } + } + + /// + /// Walks the stack cleaning up any leftover I(Async)Disposables + /// in the event of an exception on async serialization + /// + public async ValueTask DisposePendingDisposablesOnExceptionAsync() + { + Exception? exception = null; + + 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++) + { + exception = await DisposeFrame(_previous[i].CollectionEnumerator, _previous[i].AsyncEnumerator, exception).ConfigureAwait(false); + } + } + + if (exception is not null) + { + ExceptionDispatchInfo.Capture(exception).Throw(); + } + + static async ValueTask DisposeFrame(IEnumerator? collectionEnumerator, IAsyncDisposable? asyncDisposable, Exception? exception) + { + Debug.Assert(!(collectionEnumerator is not null && asyncDisposable is not null)); + + try + { + if (collectionEnumerator is IDisposable disposable) + { + disposable.Dispose(); + } + else if (asyncDisposable is not null) + { + await asyncDisposable.DisposeAsync().ConfigureAwait(false); + } + } + catch (Exception e) + { + exception = e; + } + + return exception; + } + } + // Return a property path as a simple JSONPath using dot-notation when possible. When special characters are present, bracket-notation is used: // $.x.y.z // $['PropertyName.With.Special.Chars'] 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 59533e5c542310..c28e19c9bdd4a0 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 @@ -3,6 +3,7 @@ using System.Collections; using System.Diagnostics; +using System.Threading.Tasks; using System.Text.Json.Serialization; namespace System.Text.Json @@ -15,6 +16,17 @@ internal struct WriteStackFrame /// public IEnumerator? CollectionEnumerator; + /// + /// The enumerator for resumable async disposables. + /// + public IAsyncDisposable? AsyncEnumerator; + + /// + /// The current stackframe has suspended serialization due to a pending task, + /// stored in the property. + /// + public bool AsyncEnumeratorIsPendingCompletion; + /// /// The original JsonPropertyInfo that is not changed. It contains all properties. /// @@ -113,6 +125,8 @@ public void Reset() { CollectionEnumerator = null; EnumeratorIndex = 0; + AsyncEnumerator = null; + AsyncEnumeratorIsPendingCompletion = false; IgnoreDictionaryKeyPolicy = false; JsonClassInfo = null!; OriginalDepth = 0; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs index 34868bbf4edef1..355cbd99ced919 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs @@ -26,6 +26,13 @@ public static void ThrowNotSupportedException_SerializationNotSupported(Type pro throw new NotSupportedException(SR.Format(SR.SerializationNotSupportedType, propertyType)); } + [DoesNotReturn] + [MethodImpl(MethodImplOptions.NoInlining)] + public static void ThrowNotSupportedException_TypeRequiresAsyncSerialization(Type propertyType) + { + throw new NotSupportedException(SR.Format(SR.TypeRequiresAsyncSerialization, propertyType)); + } + [DoesNotReturn] [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowNotSupportedException_ConstructorMaxOf64Parameters(ConstructorInfo constructorInfo, Type type) diff --git a/src/libraries/System.Text.Json/tests/Serialization/CollectionTests/CollectionTests.AsyncEnumerable.cs b/src/libraries/System.Text.Json/tests/Serialization/CollectionTests/CollectionTests.AsyncEnumerable.cs new file mode 100644 index 00000000000000..7c2b07d5203ed9 --- /dev/null +++ b/src/libraries/System.Text.Json/tests/Serialization/CollectionTests/CollectionTests.AsyncEnumerable.cs @@ -0,0 +1,320 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace System.Text.Json.Tests.Serialization +{ + public static partial class CollectionTests + { + [Theory] + [MemberData(nameof(GetAsyncEnumerableSources))] + public static async Task WriteRootLevelAsyncEnumerable(IEnumerable source, int delayInterval, int bufferSize) + { + JsonSerializerOptions options = new JsonSerializerOptions + { + DefaultBufferSize = bufferSize + }; + + string expectedJson = JsonSerializer.Serialize(source); + + using var stream = new Utf8MemoryStream(); + var asyncEnumerable = new MockedAsyncEnumerable(source, delayInterval); + await JsonSerializer.SerializeAsync(stream, asyncEnumerable, options); + + JsonTestHelper.AssertJsonEqual(expectedJson, stream.ToString()); + Assert.Equal(1, asyncEnumerable.TotalCreatedEnumerators); + Assert.Equal(1, asyncEnumerable.TotalDisposedEnumerators); + } + + [Theory] + [MemberData(nameof(GetAsyncEnumerableSources))] + public static async Task WriteNestedAsyncEnumerable(IEnumerable source, int delayInterval, int bufferSize) + { + JsonSerializerOptions options = new JsonSerializerOptions + { + DefaultBufferSize = bufferSize + }; + + string expectedJson = JsonSerializer.Serialize(new { Data = source }); + + using var stream = new Utf8MemoryStream(); + var asyncEnumerable = new MockedAsyncEnumerable(source, delayInterval); + await JsonSerializer.SerializeAsync(stream, new { Data = asyncEnumerable }, options); + + JsonTestHelper.AssertJsonEqual(expectedJson, stream.ToString()); + Assert.Equal(1, asyncEnumerable.TotalCreatedEnumerators); + Assert.Equal(1, asyncEnumerable.TotalDisposedEnumerators); + } + + [Theory] + [MemberData(nameof(GetAsyncEnumerableSources))] + public static async Task WriteNestedAsyncEnumerable_DTO(IEnumerable source, int delayInterval, int bufferSize) + { + JsonSerializerOptions options = new JsonSerializerOptions + { + DefaultBufferSize = bufferSize + }; + + string expectedJson = JsonSerializer.Serialize(new { Data = source }); + + using var stream = new Utf8MemoryStream(); + var asyncEnumerable = new MockedAsyncEnumerable(source, delayInterval); + await JsonSerializer.SerializeAsync(stream, new AsyncEnumerableDto { Data = asyncEnumerable }, options); + + JsonTestHelper.AssertJsonEqual(expectedJson, stream.ToString()); + Assert.Equal(1, asyncEnumerable.TotalCreatedEnumerators); + Assert.Equal(1, asyncEnumerable.TotalDisposedEnumerators); + } + + [Fact, OuterLoop] + public static async Task WriteAsyncEnumerable_LongRunningEnumeration_Cancellation() + { + var longRunningEnumerable = new MockedAsyncEnumerable( + source: Enumerable.Range(1, 100), + delayInterval: 1, + delay: TimeSpan.FromMinutes(1)); + + using var utf8Stream = new Utf8MemoryStream(); + using var cts = new CancellationTokenSource(delay: TimeSpan.FromSeconds(5)); + await Assert.ThrowsAsync(async () => + await JsonSerializer.SerializeAsync(utf8Stream, longRunningEnumerable, cancellationToken: cts.Token)); + + Assert.Equal(1, longRunningEnumerable.TotalCreatedEnumerators); + Assert.Equal(1, longRunningEnumerable.TotalDisposedEnumerators); + } + + public class AsyncEnumerableDto + { + public IAsyncEnumerable Data { get; set; } + } + + [Theory] + [MemberData(nameof(GetAsyncEnumerableSources))] + public static async Task WriteSequentialNestedAsyncEnumerables(IEnumerable source, int delayInterval, int bufferSize) + { + JsonSerializerOptions options = new JsonSerializerOptions + { + DefaultBufferSize = bufferSize + }; + + string expectedJson = JsonSerializer.Serialize(new { Data1 = source, Data2 = source }); + + using var stream = new Utf8MemoryStream(); + var asyncEnumerable = new MockedAsyncEnumerable(source, delayInterval); + await JsonSerializer.SerializeAsync(stream, new { Data1 = asyncEnumerable, Data2 = asyncEnumerable }, options); + + JsonTestHelper.AssertJsonEqual(expectedJson, stream.ToString()); + Assert.Equal(2, asyncEnumerable.TotalCreatedEnumerators); + Assert.Equal(2, asyncEnumerable.TotalDisposedEnumerators); + } + + [Theory] + [MemberData(nameof(GetAsyncEnumerableSources))] + public static async Task WriteAsyncEnumerableOfAsyncEnumerables(IEnumerable source, int delayInterval, int bufferSize) + { + JsonSerializerOptions options = new JsonSerializerOptions + { + DefaultBufferSize = bufferSize + }; + + const int OuterEnumerableCount = 5; + string expectedJson = JsonSerializer.Serialize(Enumerable.Repeat(source, OuterEnumerableCount)); + + var innerAsyncEnumerable = new MockedAsyncEnumerable(source, delayInterval); + var outerAsyncEnumerable = + new MockedAsyncEnumerable>( + Enumerable.Repeat(innerAsyncEnumerable, OuterEnumerableCount), delayInterval); + + using var stream = new Utf8MemoryStream(); + await JsonSerializer.SerializeAsync(stream, outerAsyncEnumerable, options); + + JsonTestHelper.AssertJsonEqual(expectedJson, stream.ToString()); + Assert.Equal(1, outerAsyncEnumerable.TotalCreatedEnumerators); + Assert.Equal(1, outerAsyncEnumerable.TotalDisposedEnumerators); + Assert.Equal(OuterEnumerableCount, innerAsyncEnumerable.TotalCreatedEnumerators); + Assert.Equal(OuterEnumerableCount, innerAsyncEnumerable.TotalDisposedEnumerators); + } + + [Fact] + public static void WriteRootLevelAsyncEnumerableSync_ThrowsNotSupportedException() + { + IAsyncEnumerable asyncEnumerable = new MockedAsyncEnumerable(Enumerable.Range(1, 10)); + Assert.Throws(() => JsonSerializer.Serialize(asyncEnumerable)); + } + + [Fact] + public static void WriteNestedAsyncEnumerableSync_ThrowsNotSupportedException() + { + IAsyncEnumerable asyncEnumerable = new MockedAsyncEnumerable(Enumerable.Range(1, 10)); + Assert.Throws(() => JsonSerializer.Serialize(new { Data = asyncEnumerable })); + } + + [Fact] + public static async Task WriteAsyncEnumerable_ElementSerializationThrows_ShouldDisposeEnumerator() + { + using var stream = new Utf8MemoryStream(); + var asyncEnumerable = new MockedAsyncEnumerable>(Enumerable.Repeat(ThrowingEnumerable(), 2)); + + await Assert.ThrowsAsync(() => JsonSerializer.SerializeAsync(stream, new { Data = asyncEnumerable })); + Assert.Equal(1, asyncEnumerable.TotalCreatedEnumerators); + Assert.Equal(1, asyncEnumerable.TotalDisposedEnumerators); + + static IEnumerable ThrowingEnumerable() + { + yield return 0; + throw new DivideByZeroException(); + } + } + + [Fact] + public static async Task ReadRootLevelAsyncEnumerable() + { + var utf8Stream = new Utf8MemoryStream("[0,1,2,3,4]"); + + IAsyncEnumerable result = await JsonSerializer.DeserializeAsync>(utf8Stream); + Assert.Equal(new int[] { 0, 1, 2, 3, 4 }, await result.ToListAsync()); + } + + [Fact] + public static async Task ReadNestedAsyncEnumerable() + { + var utf8Stream = new Utf8MemoryStream(@"{ ""Data"" : [0,1,2,3,4] }"); + + var result = await JsonSerializer.DeserializeAsync>(utf8Stream); + Assert.Equal(new int[] { 0, 1, 2, 3, 4 }, await result.Data.ToListAsync()); + } + + [Fact] + public static async Task ReadAsyncEnumerableOfAsyncEnumerables() + { + var utf8Stream = new Utf8MemoryStream("[[0,1,2,3,4], []]"); + + var result = await JsonSerializer.DeserializeAsync>>(utf8Stream); + var resultArray = await result.ToListAsync(); + + Assert.Equal(2, resultArray.Count); + Assert.Equal(new int[] { 0, 1, 2, 3, 4 }, await resultArray[0].ToListAsync()); + Assert.Equal(Array.Empty(), await resultArray[1].ToListAsync()); + } + + [Fact] + public static async Task ReadRootLevelAsyncEnumerableDerivative_ThrowsNotSupportedException() + { + var utf8Stream = new Utf8MemoryStream("[0,1,2,3,4]"); + await Assert.ThrowsAsync(async () => await JsonSerializer.DeserializeAsync>(utf8Stream)); + } + + public static IEnumerable GetAsyncEnumerableSources() + { + yield return WrapArgs(Enumerable.Empty(), 0, 1); + yield return WrapArgs(Enumerable.Range(0, 20), 0, 1); + yield return WrapArgs(Enumerable.Range(0, 100), 20, 20); + yield return WrapArgs(Enumerable.Range(0, 1000), 20, 20); + yield return WrapArgs(Enumerable.Range(0, 100).Select(i => $"lorem ipsum dolor: {i}"), 20, 100); + yield return WrapArgs(Enumerable.Range(0, 10).Select(i => new { Field1 = i, Field2 = $"lorem ipsum dolor: {i}", Field3 = i % 2 == 0 }), 3, 100); + yield return WrapArgs(Enumerable.Range(0, 100).Select(i => new { Field1 = i, Field2 = $"lorem ipsum dolor: {i}", Field3 = i % 2 == 0 }), 20, 100); + + static object[] WrapArgs(IEnumerable source, int delayInterval, int bufferSize) => new object[]{ source, delayInterval, bufferSize }; + } + + private static async Task> ToListAsync(this IAsyncEnumerable source) + { + var list = new List(); + await foreach (T item in source) + { + list.Add(item); + } + return list; + } + + private class MockedAsyncEnumerable : IAsyncEnumerable, IEnumerable + { + private readonly IEnumerable _source; + private readonly TimeSpan _delay; + private readonly int _delayInterval; + + internal int TotalCreatedEnumerators { get; private set; } + internal int TotalDisposedEnumerators { get; private set; } + internal int TotalEnumeratedElements { get; private set; } + + public MockedAsyncEnumerable(IEnumerable source, int delayInterval = 0, TimeSpan? delay = null) + { + _source = source; + _delay = delay ?? TimeSpan.FromMilliseconds(20); + _delayInterval = delayInterval; + } + + public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + { + return new MockedAsyncEnumerator(this, cancellationToken); + } + + // Enumerator class required to instrument IAsyncDisposable calls + private class MockedAsyncEnumerator : IAsyncEnumerator + { + private readonly MockedAsyncEnumerable _enumerable; + private IAsyncEnumerator _innerEnumerator; + + public MockedAsyncEnumerator(MockedAsyncEnumerable enumerable, CancellationToken token) + { + _enumerable = enumerable; + _innerEnumerator = enumerable.GetAsyncEnumeratorInner(token); + } + + public TElement Current => _innerEnumerator.Current; + public ValueTask DisposeAsync() + { + _enumerable.TotalDisposedEnumerators++; + return _innerEnumerator.DisposeAsync(); + } + + public ValueTask MoveNextAsync() => _innerEnumerator.MoveNextAsync(); + } + + private async IAsyncEnumerator GetAsyncEnumeratorInner(CancellationToken cancellationToken = default) + { + TotalCreatedEnumerators++; + int i = 0; + foreach (TElement element in _source) + { + if (i > 0 && _delayInterval > 0 && i % _delayInterval == 0) + { + await Task.Delay(_delay, cancellationToken); + } + + if (cancellationToken.IsCancellationRequested) + { + yield break; + } + + TotalEnumeratedElements++; + yield return element; + i++; + } + } + + public IEnumerator GetEnumerator() => throw new InvalidOperationException("Collection should not be enumerated synchronously."); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + private class Utf8MemoryStream : MemoryStream + { + public Utf8MemoryStream() : base() + { + } + + public Utf8MemoryStream(string text) : base(Encoding.UTF8.GetBytes(text)) + { + } + + public override string ToString() => Encoding.UTF8.GetString(ToArray()); + } + } +} diff --git a/src/libraries/System.Text.Json/tests/Serialization/Stream.DeserializeAsyncEnumerable.cs b/src/libraries/System.Text.Json/tests/Serialization/Stream.DeserializeAsyncEnumerable.cs new file mode 100644 index 00000000000000..27db96a7877b29 --- /dev/null +++ b/src/libraries/System.Text.Json/tests/Serialization/Stream.DeserializeAsyncEnumerable.cs @@ -0,0 +1,148 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace System.Text.Json.Serialization.Tests +{ + public static partial class StreamTests_DeserializeAsyncEnumerable + { + [Theory] + [InlineData(0, 1)] + [InlineData(1, 1)] + [InlineData(10, 1)] + [InlineData(100, 1)] + [InlineData(1000, 1)] + [InlineData(1000, 1000)] + [InlineData(1000, 32000)] + public static async Task DeserializeAsyncEnumerable_ReadSimpleObjectAsync(int count, int bufferSize) + { + JsonSerializerOptions options = new JsonSerializerOptions + { + DefaultBufferSize = bufferSize + }; + + using var stream = new MemoryStream(GenerateJsonArray(count)); + + int callbackCount = 0; + await foreach(SimpleTestClass item in JsonSerializer.DeserializeAsyncEnumerable(stream, options)) + { + Assert.Equal(callbackCount, item.MyInt32); + + item.MyInt32 = 2; // Put correct value back for Verify() + item.Verify(); + + callbackCount++; + } + + Assert.Equal(count, callbackCount); + + static byte[] GenerateJsonArray(int count) + { + SimpleTestClass[] collection = new SimpleTestClass[count]; + for (int i = 0; i < collection.Length; i++) + { + var obj = new SimpleTestClass(); + obj.Initialize(); + obj.MyInt32 = i; // verify order correctness + collection[i] = obj; + } + + return JsonSerializer.SerializeToUtf8Bytes(collection); + } + } + + [Theory] + [MemberData(nameof(GetAsyncEnumerableSources))] + public static async Task DeserializeAsyncEnumerable_ReadSourceAsync(IEnumerable source, int bufferSize) + { + JsonSerializerOptions options = new JsonSerializerOptions + { + DefaultBufferSize = bufferSize + }; + + byte[] data = JsonSerializer.SerializeToUtf8Bytes(source); + + using var stream = new MemoryStream(data); + List results = await JsonSerializer.DeserializeAsyncEnumerable(stream, options).ToListAsync(); + Assert.Equal(source, results); + } + + [Fact] + public static void DeserializeAsyncEnumerable_NullStream_ThrowsArgumentNullException() + { + AssertExtensions.Throws("utf8Json", () => JsonSerializer.DeserializeAsyncEnumerable(utf8Json: null)); + } + + [Fact] + public static async Task DeserializeAsyncEnumerable_CancellationToken_ThrowsOnCancellation() + { + JsonSerializerOptions options = new JsonSerializerOptions + { + DefaultBufferSize = 1 + }; + + byte[] data = JsonSerializer.SerializeToUtf8Bytes(Enumerable.Range(1, 100)); + + var token = new CancellationToken(canceled: true); + using var stream = new MemoryStream(data); + var cancellableAsyncEnumerable = JsonSerializer.DeserializeAsyncEnumerable(stream, options, token); + + await Assert.ThrowsAsync(async () => + { + await foreach (int element in cancellableAsyncEnumerable) + { + } + }); + } + + [Fact] + public static async Task DeserializeAsyncEnumerable_EnumeratorWithCancellationToken_ThrowsOnCancellation() + { + JsonSerializerOptions options = new JsonSerializerOptions + { + DefaultBufferSize = 1 + }; + + byte[] data = JsonSerializer.SerializeToUtf8Bytes(Enumerable.Range(1, 100)); + + var token = new CancellationToken(canceled: true); + using var stream = new MemoryStream(data); + var cancellableAsyncEnumerable = JsonSerializer.DeserializeAsyncEnumerable(stream, options).WithCancellation(token); + + await Assert.ThrowsAsync(async () => + { + await foreach (int element in cancellableAsyncEnumerable) + { + } + }); + } + + public static IEnumerable GetAsyncEnumerableSources() + { + yield return WrapArgs(Enumerable.Empty(), 1); + yield return WrapArgs(Enumerable.Range(0, 20), 1); + yield return WrapArgs(Enumerable.Range(0, 100), 20); + yield return WrapArgs(Enumerable.Range(0, 100).Select(i => $"lorem ipsum dolor: {i}"), 500); + yield return WrapArgs(Enumerable.Range(0, 10).Select(i => new { Field1 = i, Field2 = $"lorem ipsum dolor: {i}", Field3 = i % 2 == 0 }), 100); + yield return WrapArgs(Enumerable.Range(0, 100).Select(i => new { Field1 = i, Field2 = $"lorem ipsum dolor: {i}", Field3 = i % 2 == 0 }), 500); + + static object[] WrapArgs(IEnumerable source, int bufferSize) => new object[] { source, bufferSize }; + } + + private static async Task> ToListAsync(this IAsyncEnumerable source) + { + var list = new List(); + await foreach (T item in source) + { + list.Add(item); + } + return list; + } + } +} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests.csproj b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests.csproj index dbebf9b1814f3a..26116948c43104 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests.csproj +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests.csproj @@ -40,6 +40,7 @@ + @@ -123,6 +124,7 @@ + diff --git a/src/libraries/System.Threading.Tasks.Dataflow/tests/Dataflow/DataflowBlockExtensionTests.cs b/src/libraries/System.Threading.Tasks.Dataflow/tests/Dataflow/DataflowBlockExtensionTests.cs index d761c6be0f756b..d2a46b41ed236a 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/tests/Dataflow/DataflowBlockExtensionTests.cs +++ b/src/libraries/System.Threading.Tasks.Dataflow/tests/Dataflow/DataflowBlockExtensionTests.cs @@ -1123,7 +1123,7 @@ public async Task TestReceiveAsync_ManyInOrder() [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/38817", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoInterpreter))] - [PlatformSpecific(~TestPlatforms.Browser)] // uses a lot of stack + [SkipOnPlatform(TestPlatforms.Browser, "uses a lot of stack")] public async Task TestReceiveAsync_LongChain() { const int Length = 10000; @@ -1925,7 +1925,7 @@ public async Task TestOutputAvailableAsync_DataAfterCompletion() [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/38817", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoInterpreter))] - [PlatformSpecific(~TestPlatforms.Browser)] // uses a lot of stack + [SkipOnPlatform(TestPlatforms.Browser, "uses a lot of stack")] public async Task TestOutputAvailableAsync_LongSequence() { const int iterations = 10000; // enough to stack overflow if there's a problem diff --git a/src/libraries/System.Threading.Thread/tests/ThreadTests.cs b/src/libraries/System.Threading.Thread/tests/ThreadTests.cs index 4e3e32741a9e22..a0bae4654dd346 100644 --- a/src/libraries/System.Threading.Thread/tests/ThreadTests.cs +++ b/src/libraries/System.Threading.Thread/tests/ThreadTests.cs @@ -160,7 +160,7 @@ public static IEnumerable ApartmentStateTest_MemberData() [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] [ActiveIssue("https://github.com/dotnet/runtime/issues/34543", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] - [PlatformSpecific(~TestPlatforms.Browser)] // System.Diagnostics.Process is not supported on this platform. + [SkipOnPlatform(TestPlatforms.Browser, "System.Diagnostics.Process is not supported on this platform.")] [InlineData("STAMain.exe", "GetApartmentStateTest")] [InlineData("STAMain.exe", "SetApartmentStateTest")] [InlineData("STAMain.exe", "WaitAllNotSupportedOnSta_Test0")] diff --git a/src/libraries/System.Threading.Timer/tests/TimerFiringTests.cs b/src/libraries/System.Threading.Timer/tests/TimerFiringTests.cs index a0f185089856f0..785fc4cc6b18bd 100644 --- a/src/libraries/System.Threading.Timer/tests/TimerFiringTests.cs +++ b/src/libraries/System.Threading.Timer/tests/TimerFiringTests.cs @@ -284,7 +284,7 @@ select Task.Run(async () => })); } - [PlatformSpecific(~TestPlatforms.OSX & ~TestPlatforms.Browser)] // macOS and Browser in CI appears to have a lot more variation + [SkipOnPlatform(TestPlatforms.OSX | TestPlatforms.Browser, "macOS and Browser in CI appears to have a lot more variation")] [OuterLoop("Takes several seconds")] [Theory] // selection based on 333ms threshold used by implementation [InlineData(new int[] { 15 })] diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index 611e2db17e6e1d..e894ba1c091b01 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -205,7 +205,18 @@ + + + + + + + + + + + diff --git a/src/mono/Directory.Build.props b/src/mono/Directory.Build.props index 36fb3de4103c82..9f8a62f110d74c 100644 --- a/src/mono/Directory.Build.props +++ b/src/mono/Directory.Build.props @@ -13,8 +13,8 @@ - 8.0 - 9.0 + 10.0 + 10.0 2.0 5.1 10.13 diff --git a/src/mono/mono.proj b/src/mono/mono.proj index 5580d6098e1522..2a296412d05dd1 100644 --- a/src/mono/mono.proj +++ b/src/mono/mono.proj @@ -96,11 +96,11 @@ <_MonoUseNinja Condition="'$(Ninja)' == 'true' or '$(_MonoFindNinjaExitCode)' == '0' or ('$(OS)' == 'Windows_NT' and '$(Ninja)' == '')">true - + - + @@ -413,7 +413,7 @@ <_MonoCMakeConfigureCommand Condition="'$(TargetsBrowser)' != 'true' and '$(_MonoRunInitCompiler)' != 'false' and '$(OS)' == 'Windows_NT'">call "$(RepositoryEngineeringDir)native\init-vs-env.cmd" $(_CompilerTargetArch) && cd /D "$(MonoObjDir)" && @(_MonoBuildEnv, ' ') $(_MonoCMakeConfigureCommand) <_MonoCMakeConfigureCommand Condition="'$(TargetsBrowser)' != 'true' and '$(_MonoRunInitCompiler)' == 'false'">$(_MonoCCOption) $(_MonoCXXOption) @(_MonoBuildEnv, ' ') $(_MonoCMakeConfigureCommand) <_MonoCMakeConfigureCommand Condition="'$(TargetsBrowser)' == 'true' and '$(OS)' != 'Windows_NT'">bash -c 'source $(EMSDK_PATH)/emsdk_env.sh 2>&1 && emcmake $(_MonoCMakeConfigureCommand)' - <_MonoCMakeConfigureCommand Condition="'$(TargetsBrowser)' == 'true' and '$(OS)' == 'Windows_NT'">call "$([MSBuild]::NormalizePath('$(EMSDK_PATH)', 'emsdk_env.bat'))" && emcmake $(_MonoCMakeConfigureCommand) + <_MonoCMakeConfigureCommand Condition="'$(TargetsBrowser)' == 'true' and '$(OS)' == 'Windows_NT'">call "$(RepositoryEngineeringDir)native\init-vs-env.cmd" && call "$([MSBuild]::NormalizePath('$(EMSDK_PATH)', 'emsdk_env.bat'))" && emcmake $(_MonoCMakeConfigureCommand) <_MonoCMakeBuildCommand>cmake --build . --target install --config $(Configuration) <_MonoCMakeBuildCommand Condition="'$(MonoVerboseBuild)' == 'true'">$(_MonoCMakeBuildCommand) --verbose diff --git a/src/mono/mono/eglib/goutput.c b/src/mono/mono/eglib/goutput.c index b54b1e0d8b5bd6..82ba7c09d03d0b 100644 --- a/src/mono/mono/eglib/goutput.c +++ b/src/mono/mono/eglib/goutput.c @@ -322,28 +322,49 @@ default_stderr_handler (const gchar *message) } -#elif defined(HOST_IOS) -#include +#elif defined(HOST_IOS) || defined(HOST_TVOS) || defined(HOST_WATCHOS) || defined(HOST_MACCAT) +#include static int -to_asl_priority (GLogLevelFlags log_level) +to_os_log_priority (GLogLevelFlags log_level) { switch (log_level & G_LOG_LEVEL_MASK) { - case G_LOG_LEVEL_ERROR: return ASL_LEVEL_CRIT; - case G_LOG_LEVEL_CRITICAL: return ASL_LEVEL_ERR; - case G_LOG_LEVEL_WARNING: return ASL_LEVEL_WARNING; - case G_LOG_LEVEL_MESSAGE: return ASL_LEVEL_NOTICE; - case G_LOG_LEVEL_INFO: return ASL_LEVEL_INFO; - case G_LOG_LEVEL_DEBUG: return ASL_LEVEL_DEBUG; + case G_LOG_LEVEL_ERROR: return OS_LOG_TYPE_ERROR; + case G_LOG_LEVEL_CRITICAL: return OS_LOG_TYPE_ERROR; + case G_LOG_LEVEL_WARNING: return OS_LOG_TYPE_DEFAULT; + case G_LOG_LEVEL_MESSAGE: return OS_LOG_TYPE_DEFAULT; + case G_LOG_LEVEL_INFO: return OS_LOG_TYPE_DEFAULT; + case G_LOG_LEVEL_DEBUG: return OS_LOG_TYPE_DEFAULT; } - return ASL_LEVEL_ERR; + return OS_LOG_TYPE_ERROR; } +static const char * +to_log_level_name (GLogLevelFlags log_level) +{ + switch (log_level & G_LOG_LEVEL_MASK) + { + case G_LOG_LEVEL_ERROR: return "error"; + case G_LOG_LEVEL_CRITICAL: return "critical"; + case G_LOG_LEVEL_WARNING: return "warning"; + case G_LOG_LEVEL_MESSAGE: return "message"; + case G_LOG_LEVEL_INFO: return "info"; + case G_LOG_LEVEL_DEBUG: return "debug"; + } + return "unknown"; +} + +// keep in sync with mono_log_write_os_log void g_log_default_handler (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer unused_data) { - asl_log (NULL, NULL, to_asl_priority (log_level), "%s", message); + os_log_with_type (OS_LOG_DEFAULT, to_os_log_priority (log_level), "%{public}s%{public}s%{public}s: %{public}s", + log_domain != NULL ? log_domain : "", + log_domain != NULL ? ": " : "", + to_log_level_name(log_level), + message); + if (log_level & fatal) g_assert_abort (); } @@ -351,13 +372,13 @@ g_log_default_handler (const gchar *log_domain, GLogLevelFlags log_level, const static void default_stdout_handler (const gchar *message) { - asl_log (NULL, NULL, ASL_LEVEL_WARNING, "%s", message); + os_log (OS_LOG_DEFAULT, "%{public}s", message); } static void default_stderr_handler (const gchar *message) { - asl_log (NULL, NULL, ASL_LEVEL_WARNING, "%s", message); + os_log_error (OS_LOG_DEFAULT, "%{public}s", message); } #else diff --git a/src/mono/mono/mini/aot-runtime.c b/src/mono/mono/mini/aot-runtime.c index 2152352d767d25..8d5e23c632b360 100644 --- a/src/mono/mono/mini/aot-runtime.c +++ b/src/mono/mono/mini/aot-runtime.c @@ -3397,6 +3397,7 @@ sort_methods (MonoAotModule *amodule) msort_method_addresses (methods, method_indexes, methods_len); for (int i = 0; i < methods_len -1; ++i) g_assert (methods [i] <= methods [i + 1]); + amodule->sorted_methods_len = methods_len; if (mono_atomic_cas_ptr ((gpointer*)&amodule->sorted_methods, methods, NULL) != NULL) /* Somebody got in before us */ diff --git a/src/mono/mono/mini/llvm-runtime.cpp b/src/mono/mono/mini/llvm-runtime.cpp index 9982cccfb0f983..f4092d444d3204 100644 --- a/src/mono/mono/mini/llvm-runtime.cpp +++ b/src/mono/mono/mini/llvm-runtime.cpp @@ -2,6 +2,11 @@ #include "llvm-runtime.h" #include +#include + +#include +#include +#include "trace.h" extern "C" { @@ -10,6 +15,9 @@ mono_llvm_cpp_throw_exception (void) { gint32 *ex = NULL; + if (mono_trace_is_enabled ()) + mono_runtime_printf_err ("Native Stacktrace (mono_llvm_cpp_throw_exception)\n"); + /* The generated code catches an int32* */ throw ex; } diff --git a/src/mono/mono/mini/mini-exceptions.c b/src/mono/mono/mini/mini-exceptions.c index ed98a849d04d17..58a089bd494411 100644 --- a/src/mono/mono/mini/mini-exceptions.c +++ b/src/mono/mono/mini/mini-exceptions.c @@ -3445,7 +3445,7 @@ mono_print_thread_dump_internal (void *sigctx, MonoContext *start_ctx) mono_walk_stack_with_ctx (print_stack_frame_to_string, &ctx, MONO_UNWIND_LOOKUP_ALL, text); #if HOST_WASM - mono_runtime_printf_err ("%s", text->str); //to print the native callstack + mono_runtime_printf_err ("%s\n", text->str); //to print the native callstack #else mono_runtime_printf ("%s", text->str); #endif diff --git a/src/mono/mono/mini/mini-llvm.c b/src/mono/mono/mini/mini-llvm.c index 26d48ea4131e1d..d0ab58de46dbe8 100644 --- a/src/mono/mono/mini/mini-llvm.c +++ b/src/mono/mono/mini/mini-llvm.c @@ -2854,7 +2854,7 @@ emit_get_method (MonoLLVMModule *module) { LLVMModuleRef lmodule = module->lmodule; LLVMValueRef func, switch_ins, m; - LLVMBasicBlockRef entry_bb, fail_bb, bb, code_start_bb, code_end_bb; + LLVMBasicBlockRef entry_bb, fail_bb, bb, code_start_bb, code_end_bb, main_bb; LLVMBasicBlockRef *bbs = NULL; LLVMTypeRef rtype; LLVMBuilderRef builder = LLVMCreateBuilder (); @@ -2874,12 +2874,13 @@ emit_get_method (MonoLLVMModule *module) rtype = LLVMPointerType (LLVMInt8Type (), 0); + int table_len = module->max_method_idx + 1; + if (emit_table) { LLVMTypeRef table_type; LLVMValueRef *table_elems; char *table_name; - int table_len = module->max_method_idx + 1; table_type = LLVMArrayType (rtype, table_len); table_name = g_strdup_printf ("%s_method_table", module->global_prefix); table = LLVMAddGlobal (lmodule, table_type, table_name); @@ -2926,13 +2927,21 @@ emit_get_method (MonoLLVMModule *module) if (emit_table) { /* + * Because table_len is computed using the method indexes available for us, it + * might not include methods which are not compiled because of AOT profiles. + * So table_len can be smaller than info->nmethods. Add a bounds check because + * of that. * switch (index) { * case -1: return code_start; * case -2: return code_end; - * default: return method_table [index]; + * default: return index < table_len ? method_table [index] : 0; */ - LLVMBasicBlockRef default_bb = LLVMAppendBasicBlock (func, "DEFAULT"); - LLVMPositionBuilderAtEnd (builder, default_bb); + fail_bb = LLVMAppendBasicBlock (func, "FAIL"); + LLVMPositionBuilderAtEnd (builder, fail_bb); + LLVMBuildRet (builder, LLVMBuildIntToPtr (builder, LLVMConstInt (LLVMInt32Type (), 0, FALSE), rtype, "")); + + main_bb = LLVMAppendBasicBlock (func, "MAIN"); + LLVMPositionBuilderAtEnd (builder, main_bb); LLVMValueRef base = table; LLVMValueRef indexes [2]; indexes [0] = LLVMConstInt (LLVMInt32Type (), 0, FALSE); @@ -2941,6 +2950,11 @@ emit_get_method (MonoLLVMModule *module) LLVMValueRef res = mono_llvm_build_load (builder, addr, "", FALSE); LLVMBuildRet (builder, res); + LLVMBasicBlockRef default_bb = LLVMAppendBasicBlock (func, "DEFAULT"); + LLVMPositionBuilderAtEnd (builder, default_bb); + LLVMValueRef cmp = LLVMBuildICmp (builder, LLVMIntSGE, LLVMGetParam (func, 0), LLVMConstInt (LLVMInt32Type (), table_len, FALSE), ""); + LLVMBuildCondBr (builder, cmp, fail_bb, main_bb); + LLVMPositionBuilderAtEnd (builder, entry_bb); switch_ins = LLVMBuildSwitch (builder, LLVMGetParam (func, 0), default_bb, 0); diff --git a/src/mono/mono/mini/mini-trampolines.c b/src/mono/mono/mini/mini-trampolines.c index 3767a9d8e004af..989f07efae94de 100644 --- a/src/mono/mono/mini/mini-trampolines.c +++ b/src/mono/mono/mini/mini-trampolines.c @@ -153,13 +153,6 @@ mono_create_static_rgctx_trampoline (MonoMethod *m, gpointer addr) } #endif -// FIXME: Is this still needed ? -static gboolean -owns_vtable_slot (gpointer vtable_slot) -{ - return mono_mem_manager_mp_contains_addr (mono_mem_manager_get_ambient (), vtable_slot); -} - gpointer mono_create_ftnptr_arg_trampoline (gpointer arg, gpointer addr) { @@ -514,9 +507,7 @@ common_call_trampoline (host_mgreg_t *regs, guint8 *code, MonoMethod *m, MonoVTa /* * We found AOT compiled code for the method, skip the rest. */ - if (owns_vtable_slot (vtable_slot)) - *vtable_slot = addr; - + *vtable_slot = addr; return mono_create_ftnptr (addr); } @@ -697,7 +688,7 @@ common_call_trampoline (host_mgreg_t *regs, guint8 *code, MonoMethod *m, MonoVTa vtable_slot = orig_vtable_slot; if (vtable_slot) { - if (vtable_slot_to_patch && (mono_aot_is_got_entry (code, (guint8*)vtable_slot_to_patch) || owns_vtable_slot (vtable_slot_to_patch))) { + if (vtable_slot_to_patch) { g_assert (*vtable_slot_to_patch); *vtable_slot_to_patch = mono_get_addr_from_ftnptr (addr); } @@ -830,9 +821,7 @@ mono_vcall_trampoline (host_mgreg_t *regs, guint8 *code, int slot, guint8 *tramp addr = mono_aot_get_method_from_vt_slot (vt, slot, error); goto_if_nok (error, leave); if (addr && !m_class_is_valuetype (vt->klass)) { - if (owns_vtable_slot (vtable_slot)) - *vtable_slot = addr; - + *vtable_slot = addr; res = mono_create_ftnptr (addr); goto leave; } diff --git a/src/mono/mono/mini/trace.h b/src/mono/mono/mini/trace.h index 19b443c8bb957e..a7ae09a586bf0c 100644 --- a/src/mono/mono/mini/trace.h +++ b/src/mono/mono/mini/trace.h @@ -21,7 +21,10 @@ void mono_trace_tail_method (MonoMethod *method, MonoJitInfo *ji, MonoMethod *target); void mono_trace_enable (gboolean enable); + +G_EXTERN_C gboolean mono_trace_is_enabled (void); + gboolean mono_trace_eval_exception (MonoClass *klass); #endif /* __MONO_TRACE_H__ */ diff --git a/src/mono/mono/utils/mono-log-darwin.c b/src/mono/mono/utils/mono-log-darwin.c index 2cb41b1481e020..039dc8e79d16d8 100644 --- a/src/mono/mono/utils/mono-log-darwin.c +++ b/src/mono/mono/utils/mono-log-darwin.c @@ -5,106 +5,61 @@ */ #include -#if (defined(HOST_WATCHOS) && (__WATCH_OS_VERSION_MIN_REQUIRED >= __WATCHOS_3_0)) || defined(HOST_MACCAT) -/* emitted by clang: - * > /Users/lewurm/work/mono-watch4/mono/utils/mono-log-darwin.c:35:2: error: 'asl_log' is \ - * > deprecated: first deprecated in watchOS 3.0 - os_log(3) has replaced \ - * > asl(3) [-Werror,-Wdeprecated-declarations] - */ - -/* untested stuff: */ +#if defined(HOST_IOS) || defined(HOST_TVOS) || defined(HOST_WATCHOS) || defined(HOST_MACCAT) #include #include "mono-logger-internals.h" -void -mono_log_open_asl (const char *path, void *userData) -{ -} void -mono_log_write_asl (const char *log_domain, GLogLevelFlags level, mono_bool hdr, const char *message) +mono_log_open_os_log (const char *path, void *userData) { - switch (level & G_LOG_LEVEL_MASK) - { - case G_LOG_LEVEL_MESSAGE: - os_log (OS_LOG_DEFAULT, "%s%s%s\n", - log_domain != NULL ? log_domain : "", - log_domain != NULL ? ": " : "", - message); - break; - case G_LOG_LEVEL_INFO: - os_log_info (OS_LOG_DEFAULT, "%s%s%s\n", - log_domain != NULL ? log_domain : "", - log_domain != NULL ? ": " : "", - message); - break; - case G_LOG_LEVEL_DEBUG: - os_log_debug (OS_LOG_DEFAULT, "%s%s%s\n", - log_domain != NULL ? log_domain : "", - log_domain != NULL ? ": " : "", - message); - break; - case G_LOG_LEVEL_ERROR: - case G_LOG_LEVEL_WARNING: - os_log_error (OS_LOG_DEFAULT, "%s%s%s\n", - log_domain != NULL ? log_domain : "", - log_domain != NULL ? ": " : "", - message); - case G_LOG_LEVEL_CRITICAL: - default: - os_log_fault (OS_LOG_DEFAULT, "%s%s%s\n", - log_domain != NULL ? log_domain : "", - log_domain != NULL ? ": " : "", - message); - break; - } - - if (level & G_LOG_LEVEL_ERROR) - abort(); } -void -mono_log_close_asl () -{ -} - -#elif defined(HOST_IOS) - -#include -#include "mono-logger-internals.h" static int -to_asl_priority (GLogLevelFlags log_level) +to_os_log_priority (GLogLevelFlags log_level) { switch (log_level & G_LOG_LEVEL_MASK) { - case G_LOG_LEVEL_ERROR: return ASL_LEVEL_CRIT; - case G_LOG_LEVEL_CRITICAL: return ASL_LEVEL_ERR; - case G_LOG_LEVEL_WARNING: return ASL_LEVEL_WARNING; - case G_LOG_LEVEL_MESSAGE: return ASL_LEVEL_NOTICE; - case G_LOG_LEVEL_INFO: return ASL_LEVEL_INFO; - case G_LOG_LEVEL_DEBUG: return ASL_LEVEL_DEBUG; + case G_LOG_LEVEL_ERROR: return OS_LOG_TYPE_ERROR; + case G_LOG_LEVEL_CRITICAL: return OS_LOG_TYPE_ERROR; + case G_LOG_LEVEL_WARNING: return OS_LOG_TYPE_DEFAULT; + case G_LOG_LEVEL_MESSAGE: return OS_LOG_TYPE_DEFAULT; + case G_LOG_LEVEL_INFO: return OS_LOG_TYPE_DEFAULT; + case G_LOG_LEVEL_DEBUG: return OS_LOG_TYPE_DEFAULT; } - return ASL_LEVEL_ERR; + return OS_LOG_TYPE_ERROR; } -void -mono_log_open_asl (const char *path, void *userData) +static const char * +to_log_level_name (GLogLevelFlags log_level) { + switch (log_level & G_LOG_LEVEL_MASK) + { + case G_LOG_LEVEL_ERROR: return "error"; + case G_LOG_LEVEL_CRITICAL: return "critical"; + case G_LOG_LEVEL_WARNING: return "warning"; + case G_LOG_LEVEL_MESSAGE: return "message"; + case G_LOG_LEVEL_INFO: return "info"; + case G_LOG_LEVEL_DEBUG: return "debug"; + } + return "unknown"; } +// keep in sync with g_log_default_handler void -mono_log_write_asl (const char *log_domain, GLogLevelFlags level, mono_bool hdr, const char *message) +mono_log_write_os_log (const char *log_domain, GLogLevelFlags level, mono_bool hdr, const char *message) { - asl_log (NULL, NULL, to_asl_priority (level), "%s%s%s\n", + os_log_with_type (OS_LOG_DEFAULT, to_os_log_priority (level), "%{public}s%{public}s%{public}s: %{public}s", log_domain != NULL ? log_domain : "", log_domain != NULL ? ": " : "", + to_log_level_name(level), message); if (level & G_LOG_LEVEL_ERROR) - g_assert_abort (); + abort(); } void -mono_log_close_asl () +mono_log_close_os_log () { } diff --git a/src/mono/mono/utils/mono-logger-internals.h b/src/mono/mono/utils/mono-logger-internals.h index d32f69378ae5e3..48db7fa1750564 100644 --- a/src/mono/mono/utils/mono-logger-internals.h +++ b/src/mono/mono/utils/mono-logger-internals.h @@ -151,10 +151,10 @@ void mono_log_write_logcat (const char *log_domain, GLogLevelFlags level, mono_b void mono_log_close_logcat (void); #endif -#if defined(HOST_IOS) -void mono_log_open_asl (const char *path, void *userData); -void mono_log_write_asl (const char *log_domain, GLogLevelFlags level, mono_bool hdr, const char *message); -void mono_log_close_asl (void); +#if defined(HOST_IOS) || defined(HOST_TVOS) || defined(HOST_WATCHOS) || defined(HOST_MACCAT) +void mono_log_open_os_log (const char *path, void *userData); +void mono_log_write_os_log (const char *log_domain, GLogLevelFlags level, mono_bool hdr, const char *message); +void mono_log_close_os_log (void); #endif void mono_log_open_recorder (const char *path, void *userData); diff --git a/src/mono/mono/utils/mono-logger.c b/src/mono/mono/utils/mono-logger.c index b157a3fb787233..b688a0dda7d9ec 100644 --- a/src/mono/mono/utils/mono-logger.c +++ b/src/mono/mono/utils/mono-logger.c @@ -137,10 +137,10 @@ mono_trace_set_logdest_string (const char *dest) logger.writer = mono_log_write_logcat; logger.closer = mono_log_close_logcat; logger.dest = (char*) dest; -#elif defined (HOST_IOS) - logger.opener = mono_log_open_asl; - logger.writer = mono_log_write_asl; - logger.closer = mono_log_close_asl; +#elif defined(HOST_IOS) || defined(HOST_TVOS) || defined(HOST_WATCHOS) || defined(HOST_MACCAT) + logger.opener = mono_log_open_os_log; + logger.writer = mono_log_write_os_log; + logger.closer = mono_log_close_os_log; logger.dest = (char*) dest; #else if (dest && !strcmp("flight-recorder", dest)) { diff --git a/src/mono/sample/wasm/Directory.Build.targets b/src/mono/sample/wasm/Directory.Build.targets index 6a83bb38c13471..86e533b6054c26 100644 --- a/src/mono/sample/wasm/Directory.Build.targets +++ b/src/mono/sample/wasm/Directory.Build.targets @@ -5,4 +5,25 @@ + + + + <_ScriptExt Condition="'$(OS)' == 'Windows_NT'">.cmd + <_ScriptExt Condition="'$(OS)' != 'Windows_NT'">.sh + <_Dotnet>$(RepoRoot)dotnet$(_ScriptExt) + <_AOTFlag Condition="'$(RunAOTCompilation)' != ''">/p:RunAOTCompilation=$(RunAOTCompilation) + + + + + + + + + + + + diff --git a/src/mono/sample/wasm/browser/Wasm.Browser.Sample.csproj b/src/mono/sample/wasm/browser/Wasm.Browser.Sample.csproj index b3662f2f697bf2..2b05836627673c 100644 --- a/src/mono/sample/wasm/browser/Wasm.Browser.Sample.csproj +++ b/src/mono/sample/wasm/browser/Wasm.Browser.Sample.csproj @@ -7,4 +7,10 @@ + + + <_SampleProject>Wasm.Browser.Sample.csproj + + + diff --git a/src/mono/sample/wasm/console/Wasm.Console.Sample.csproj b/src/mono/sample/wasm/console/Wasm.Console.Sample.csproj index d73c29ef60c763..7097ef20ab9bd6 100644 --- a/src/mono/sample/wasm/console/Wasm.Console.Sample.csproj +++ b/src/mono/sample/wasm/console/Wasm.Console.Sample.csproj @@ -4,4 +4,10 @@ $(MonoProjectRoot)\wasm\runtime-test.js true + + + <_SampleProject>Wasm.Console.Sample.csproj + + + diff --git a/src/mono/wasm/README.md b/src/mono/wasm/README.md index 5affce23fd593f..3e90f47d18d9c3 100644 --- a/src/mono/wasm/README.md +++ b/src/mono/wasm/README.md @@ -4,9 +4,7 @@ This depends on `emsdk` to be installed. ## emsdk -* You can either install it yourself (https://emscripten.org/docs/getting_started/downloads.html), and set `EMSDK_PATH` to that. Make sure to have this set whenever building, or running tests for wasm. - -* Or you can run `make provision-wasm`, which will install it to `$reporoot/src/mono/wasm/emsdk`. +* You can run `make provision-wasm`, which will install it to `$reporoot/src/mono/wasm/emsdk`. Note: Irrespective of `$(EMSDK_PATH)`'s value, `provision-wasm` will always install into `$reporoot/src/mono/wasm/emsdk`. `EMSDK_PATH` is set to `$reporoot/src/mono/wasm/emsdk` by default, by the Makefile. @@ -14,6 +12,11 @@ Note: Irrespective of `$(EMSDK_PATH)`'s value, `provision-wasm` will always inst Note: `EMSDK_PATH` is set by default in `src/mono/wasm/Makefile`, so building targets from that will have it set. But you might need to set it manually if 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.12`. See [emscripten-version.txt](./emscripten-version.txt) + +Make sure to set `EMSDK_PATH` variable, whenever building, or running tests for wasm. + ### Windows dependencies Windows build [requirements](https://github.com/dotnet/runtime/blob/main/docs/workflow/requirements/windows-requirements.md) @@ -102,3 +105,17 @@ To run a test with `FooBar` in the name: (See https://docs.microsoft.com/en-us/dotnet/core/testing/selective-unit-tests?pivots=xunit for filter options) Additional arguments for `dotnet test` can be passed via `TEST_ARGS`. Though only one of `TEST_ARGS`, or `TEST_FILTER` can be used at a time. + +## Run samples + +The samples in `src/mono/sample/wasm` can be build and run like this: + +* console Hello world sample + +`dotnet build /t:RunSample console/Wasm.Console.Sample.csproj` + +* browser TestMeaning sample + +`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. diff --git a/src/mono/wasm/build/WasmApp.targets b/src/mono/wasm/build/WasmApp.targets index 7711fdbe273733..0ab461d05d8377 100644 --- a/src/mono/wasm/build/WasmApp.targets +++ b/src/mono/wasm/build/WasmApp.targets @@ -20,7 +20,12 @@ - $(WasmNativeStrip) - Whenever to strip the native executable. Defaults to true. - $(WasmLinkIcalls) - Whenever to link out unused icalls. Defaults to $(WasmBuildNative). - $(RunAOTCompilation) - Defaults to false. + - $(WasmDebugLevel) + > 0 enables debugging and sets the debug log level to debug_level + == 0 disables debugging and enables interpreter optimizations + < 0 enabled debugging and disables debug logging. + - $(WasmNativeDebugSymbols) - Build with native debug symbols, useful only with `$(RunAOTCompilation)`, or `$(WasmBuildNative)` Defaults to true. - $(WasmDedup) - Whenever to dedup generic instances when using AOT. Defaults to true. diff --git a/src/mono/wasm/wasm.proj b/src/mono/wasm/wasm.proj index 8bd6c8616fb63f..dda6fa29f62723 100644 --- a/src/mono/wasm/wasm.proj +++ b/src/mono/wasm/wasm.proj @@ -226,7 +226,7 @@ DestinationFolder="$(MicrosoftNetCoreAppRuntimePackNativeDir)src" SkipUnchangedFiles="true" /> - diff --git a/src/native/corehost/test/CMakeLists.txt b/src/native/corehost/test/CMakeLists.txt index 72ba29f816925a..e5d93fd2c030f6 100644 --- a/src/native/corehost/test/CMakeLists.txt +++ b/src/native/corehost/test/CMakeLists.txt @@ -3,3 +3,6 @@ add_subdirectory(mockcoreclr) add_subdirectory(mockhostfxr) add_subdirectory(mockhostpolicy) add_subdirectory(nativehost) +if (CLR_CMAKE_TARGET_WIN32) + add_subdirectory(typelibs) +endif() diff --git a/src/native/corehost/test/nativehost/comhost_test.cpp b/src/native/corehost/test/nativehost/comhost_test.cpp index 68d0c93b62baac..1d132f1757307e 100644 --- a/src/native/corehost/test/nativehost/comhost_test.cpp +++ b/src/native/corehost/test/nativehost/comhost_test.cpp @@ -6,6 +6,7 @@ #include #include #include +#include namespace { @@ -67,12 +68,42 @@ namespace return pal::pal_utf8string(clsid_str, &clsidVect); } + void log_hresult(HRESULT hr, std::ostream &ss) + { + if (FAILED(hr)) + ss << "(" << std::hex << std::showbase << hr << ")"; + } + void log_activation(const char *clsid, int activationNumber, int total, HRESULT hr, std::ostream &ss) { ss << "Activation of " << clsid << (FAILED(hr) ? " failed. " : " succeeded. ") << activationNumber << " of " << total; + log_hresult(hr, ss); + ss << std::endl; + } + + HRESULT load_typelib(const pal::string_t &typelib_path) + { + HRESULT hr; + ITypeLib* typelib = nullptr; + hr = LoadTypeLibEx(typelib_path.c_str(), REGKIND_NONE, &typelib); if (FAILED(hr)) - ss << "(" << std::hex << std::showbase << hr << ")"; + return hr; + typelib->Release(); + return hr; + } + + void log_typelib_load(int typelib_id, HRESULT hr, std::ostream &ss) + { + ss << "Loading type library " << typelib_id << (FAILED(hr) ? " failed. " : " succeeded. "); + log_hresult(hr, ss); + ss << std::endl; + } + + void log_default_typelib_load(HRESULT hr, std::ostream &ss) + { + ss << "Loading default type library" << (FAILED(hr) ? " failed. " : " succeeded. "); + log_hresult(hr, ss); ss << std::endl; } } @@ -165,3 +196,26 @@ bool comhost_test::errorinfo(const pal::string_t &comhost_path, const pal::strin return true; } + +bool comhost_test::typelib(const pal::string_t &comhost_path, int count) +{ + HRESULT hr; + + hr = load_typelib(comhost_path); + log_default_typelib_load(hr, std::cout); + if (FAILED(hr)) + return false; + + for (int i = 1; i < count + 1; i++) + { + // The path format for a non-default embedded TLB is 'C:\file\path\to.exe\\2' where '2' is the resource name of the tlb to load. + // See https://docs.microsoft.com/windows/win32/api/oleauto/nf-oleauto-loadtypelib#remarks for documentation on the path format. + pal::stringstream_t tlb_path; + tlb_path << comhost_path << '\\' << i; + hr = load_typelib(tlb_path.str()); + log_typelib_load(i, hr, std::cout); + if (FAILED(hr)) + return false; + } + return true; +} diff --git a/src/native/corehost/test/nativehost/comhost_test.h b/src/native/corehost/test/nativehost/comhost_test.h index 767071f2917c8f..189a0a1ebe6138 100644 --- a/src/native/corehost/test/nativehost/comhost_test.h +++ b/src/native/corehost/test/nativehost/comhost_test.h @@ -10,4 +10,6 @@ namespace comhost_test bool concurrent(const pal::string_t &comhost_path, const pal::string_t &clsid_str, int count); bool errorinfo(const pal::string_t &comhost_path, const pal::string_t &clsid_str, int count); + + bool typelib(const pal::string_t &comhost_path, int count); } diff --git a/src/native/corehost/test/nativehost/nativehost.cpp b/src/native/corehost/test/nativehost/nativehost.cpp index db90ded35593df..8987cb821ca91d 100644 --- a/src/native/corehost/test/nativehost/nativehost.cpp +++ b/src/native/corehost/test/nativehost/nativehost.cpp @@ -403,6 +403,10 @@ int main(const int argc, const pal::char_t *argv[]) { success = comhost_test::errorinfo(comhost_path, clsid_str, count); } + else if (pal::strcmp(scenario, _X("typelib")) == 0) + { + success = comhost_test::typelib(comhost_path, count); + } return success ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/src/native/corehost/test/typelibs/CMakeLists.txt b/src/native/corehost/test/typelibs/CMakeLists.txt new file mode 100644 index 00000000000000..0b21548ef96fd2 --- /dev/null +++ b/src/native/corehost/test/typelibs/CMakeLists.txt @@ -0,0 +1,30 @@ + +# Get the current list of definitions to pass to midl +get_compile_definitions(MIDL_DEFINITIONS) +get_include_directories(MIDL_INCLUDE_DIRECTORIES) +find_program(MIDL midl.exe) + +function(compile_idl idl_file tlb_out) + # Compile IDL file using MIDL + set(IDL_SOURCE ${idl_file}) + get_filename_component(IDL_NAME ${IDL_SOURCE} NAME_WE) + set(tlb_out_local "${CMAKE_CURRENT_BINARY_DIR}/${IDL_NAME}.tlb") + set("${tlb_out}" "${tlb_out_local}" PARENT_SCOPE) + + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${IDL_NAME}_i.c ${CMAKE_CURRENT_BINARY_DIR}/${IDL_NAME}.h ${tlb_out_local} + COMMAND ${MIDL} ${MIDL_INCLUDE_DIRECTORIES} + /h ${CMAKE_CURRENT_BINARY_DIR}/${IDL_NAME}.h ${MIDL_DEFINITIONS} + /out ${CMAKE_CURRENT_BINARY_DIR} + /tlb ${tlb_out_local} + ${IDL_SOURCE} + DEPENDS ${IDL_SOURCE} + COMMENT "Compiling ${IDL_SOURCE}") +endfunction() + +compile_idl(${CMAKE_CURRENT_SOURCE_DIR}/Server.idl Server_tlb) +compile_idl(${CMAKE_CURRENT_SOURCE_DIR}/Nested.idl Nested_tlb) + +add_custom_target(typelibs ALL DEPENDS "${Server_tlb}" "${Nested_tlb}") + +install(FILES "${Server_tlb}" "${Nested_tlb}" DESTINATION corehost_test) diff --git a/src/native/corehost/test/typelibs/Nested.idl b/src/native/corehost/test/typelibs/Nested.idl new file mode 100644 index 00000000000000..9fca7ec74d7760 --- /dev/null +++ b/src/native/corehost/test/typelibs/Nested.idl @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +import "oaidl.idl"; +import "ocidl.idl"; + +[ + object, + uuid(f7199267-9821-4f5b-924b-ab5246b455cd) +] +interface INested : IDispatch +{ +}; + +[ + uuid(f7c46a13-a1fc-4bf1-a61d-4502215c24e9) +] +library ComLibrary +{ + importlib("stdole2.tlb"); + [ + uuid(c82e4585-58bd-46e0-a76d-c0b6975e5984) + ] + coclass ComVisible_Nested + { + [default] interface INested; + } +} diff --git a/src/native/corehost/test/typelibs/Server.idl b/src/native/corehost/test/typelibs/Server.idl new file mode 100644 index 00000000000000..29abcd842670c0 --- /dev/null +++ b/src/native/corehost/test/typelibs/Server.idl @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +import "oaidl.idl"; +import "ocidl.idl"; + +[ + object, + uuid(27293cc8-7933-4fdf-9fde-653cbf9b55df) +] +interface IServer : IDispatch +{ +}; + +[ + uuid(20151109-a0e8-46ae-b28e-8ff2c0e72166) +] +library ComLibrary +{ + importlib("stdole2.tlb"); + [ + uuid(438968CE-5950-4FBC-90B0-E64691350DF5) + ] + coclass Server + { + [default] interface IServer; + } +} diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs index de7c0e3741b74b..d1cf986b3cccfb 100644 --- a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs +++ b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs @@ -150,7 +150,7 @@ public override bool Execute () foreach (var assembly in _assemblies) { FileCopyChecked(assembly, Path.Join(asmRootPath, Path.GetFileName(assembly)), "Assemblies"); - if (DebugLevel > 0) + if (DebugLevel != 0) { var pdb = assembly; pdb = Path.ChangeExtension(pdb, ".pdb"); @@ -173,7 +173,7 @@ public override bool Execute () foreach (var assembly in _assemblies) { config.Assets.Add(new AssemblyEntry(Path.GetFileName(assembly))); - if (DebugLevel > 0) { + if (DebugLevel != 0) { var pdb = assembly; pdb = Path.ChangeExtension(pdb, ".pdb"); if (File.Exists(pdb)) diff --git a/src/tests/Interop/COM/ComWrappers/Common.cs b/src/tests/Interop/COM/ComWrappers/Common.cs index 5d1994131e7a76..4aa98b76e9031c 100644 --- a/src/tests/Interop/COM/ComWrappers/Common.cs +++ b/src/tests/Interop/COM/ComWrappers/Common.cs @@ -21,9 +21,10 @@ class Test : ITest, ICustomQueryInterface { public static int InstanceCount = 0; + private int id; private int value = -1; - public Test() { InstanceCount++; } - ~Test() { InstanceCount--; } + public Test() { id = Interlocked.Increment(ref InstanceCount); } + ~Test() { Interlocked.Decrement(ref InstanceCount); id = -1; } public void SetValue(int i) => this.value = i; public int GetValue() => this.value; diff --git a/src/tests/Regressions/coreclr/GitHub_49982/test49982.csproj b/src/tests/Regressions/coreclr/GitHub_49982/test49982.csproj index 27de21752249f1..b7731c9fde6cfb 100644 --- a/src/tests/Regressions/coreclr/GitHub_49982/test49982.csproj +++ b/src/tests/Regressions/coreclr/GitHub_49982/test49982.csproj @@ -9,6 +9,9 @@ true + + + true diff --git a/src/tests/tracing/eventpipe/processenvironment/processenvironment.cs b/src/tests/tracing/eventpipe/processenvironment/processenvironment.cs index f0d648d3883fd2..60a1a30ee94f01 100644 --- a/src/tests/tracing/eventpipe/processenvironment/processenvironment.cs +++ b/src/tests/tracing/eventpipe/processenvironment/processenvironment.cs @@ -13,6 +13,7 @@ using Microsoft.Diagnostics.Tools.RuntimeClient; using Microsoft.Diagnostics.Tracing; using Tracing.Tests.Common; +using System.Reflection; namespace Tracing.Tests.ProcessEnvironmentValidation { @@ -20,10 +21,14 @@ public class ProcessEnvironmentValidation { public static int Main(string[] args) { + if (args.Length >= 1) + { + Console.Out.WriteLine("Subprocess started! Waiting for input..."); + var input = Console.In.ReadLine(); // will block until data is sent across stdin + Console.Out.WriteLine($"Received '{input}'. Exiting..."); + return 0; + } - Process currentProcess = Process.GetCurrentProcess(); - int pid = currentProcess.Id; - Logger.logger.Log($"Test PID: {pid}"); var testEnvPairs = new Dictionary { { "TESTKEY1", "TESTVAL1" }, @@ -31,65 +36,72 @@ public static int Main(string[] args) { "TESTKEY3", "__TEST__VAL=;--3" } }; - foreach (var (key, val) in testEnvPairs) - System.Environment.SetEnvironmentVariable(key, val); - - Stream stream = ConnectionHelper.GetStandardTransport(pid); - - // 0x04 = ProcessCommandSet, 0x02 = ProcessInfo - var processInfoMessage = new IpcMessage(0x04, 0x02); - Logger.logger.Log($"Wrote: {processInfoMessage}"); - Stream continuationStream = IpcClient.SendMessage(stream, processInfoMessage, out IpcMessage response); - Logger.logger.Log($"Received: {response}"); - - Utils.Assert(response.Header.CommandSet == 0xFF, $"Response must have Server command set. Expected: 0xFF, Received: 0x{response.Header.CommandSet:X2}"); // server - Utils.Assert(response.Header.CommandId == 0x00, $"Response must have OK command id. Expected: 0x00, Received: 0x{response.Header.CommandId:X2}"); // OK - - UInt32 continuationSizeInBytes = BitConverter.ToUInt32(response.Payload[0..4]); - Logger.logger.Log($"continuation size: {continuationSizeInBytes} bytes"); - UInt16 future = BitConverter.ToUInt16(response.Payload[4..]); - Logger.logger.Log($"future value: {future}"); - - using var memoryStream = new MemoryStream(); - Logger.logger.Log($"Starting to copy continuation"); - continuationStream.CopyTo(memoryStream); - Logger.logger.Log($"Finished copying continuation"); - byte[] envBlock = memoryStream.ToArray(); - Logger.logger.Log($"Total bytes in continuation: {envBlock.Length}"); - - Utils.Assert(envBlock.Length == continuationSizeInBytes, $"Continuation size must equal the reported size in the payload response. Expected: {continuationSizeInBytes} bytes, Received: {envBlock.Length} bytes"); - - // VALIDATE ENV - // env block is sent as Array (length-prefixed array of length-prefixed wchar strings) - int start = 0; - int end = start + 4 /* sizeof(uint32_t) */; - UInt32 envCount = BitConverter.ToUInt32(envBlock[start..end]); - Logger.logger.Log($"envCount: {envCount}"); - - var env = new Dictionary(); - for (int i = 0; i < envCount; i++) - { - start = end; - end = start + 4 /* sizeof(uint32_t) */; - UInt32 pairLength = BitConverter.ToUInt32(envBlock[start..end]); - - start = end; - end = start + ((int)pairLength * sizeof(char)); - Utils.Assert(end <= envBlock.Length, $"String end can't exceed payload size. Expected: <{envBlock.Length}, Received: {end} (decoded length: {pairLength})"); - string envPair = System.Text.Encoding.Unicode.GetString(envBlock[start..end]).TrimEnd('\0'); - int equalsIndex = envPair.IndexOf('='); - env[envPair[0..equalsIndex]] = envPair[(equalsIndex+1)..]; - } - Logger.logger.Log($"finished parsing env"); - - - foreach (var (key, val) in testEnvPairs) - Utils.Assert(env.ContainsKey(key) && env[key].Equals(val), $"Did not find test environment pair in the environment block: '{key}' = '{val}'"); - Logger.logger.Log($"Saw test values in env"); - - Utils.Assert(end == envBlock.Length, $"Full payload should have been read. Expected: {envBlock.Length}, Received: {end}"); - - return 100; + Task subprocessTask = Utils.RunSubprocess( + currentAssembly: Assembly.GetExecutingAssembly(), + environment: testEnvPairs, + duringExecution: (int pid) => + { + Logger.logger.Log($"Test PID: {pid}"); + + Stream stream = ConnectionHelper.GetStandardTransport(pid); + + // 0x04 = ProcessCommandSet, 0x02 = ProcessInfo + var processInfoMessage = new IpcMessage(0x04, 0x02); + Logger.logger.Log($"Wrote: {processInfoMessage}"); + Stream continuationStream = IpcClient.SendMessage(stream, processInfoMessage, out IpcMessage response); + Logger.logger.Log($"Received: {response}"); + + Utils.Assert(response.Header.CommandSet == 0xFF, $"Response must have Server command set. Expected: 0xFF, Received: 0x{response.Header.CommandSet:X2}"); // server + Utils.Assert(response.Header.CommandId == 0x00, $"Response must have OK command id. Expected: 0x00, Received: 0x{response.Header.CommandId:X2}"); // OK + + UInt32 continuationSizeInBytes = BitConverter.ToUInt32(response.Payload[0..4]); + Logger.logger.Log($"continuation size: {continuationSizeInBytes} bytes"); + UInt16 future = BitConverter.ToUInt16(response.Payload[4..]); + Logger.logger.Log($"future value: {future}"); + + using var memoryStream = new MemoryStream(); + Logger.logger.Log($"Starting to copy continuation"); + continuationStream.CopyTo(memoryStream); + Logger.logger.Log($"Finished copying continuation"); + byte[] envBlock = memoryStream.ToArray(); + Logger.logger.Log($"Total bytes in continuation: {envBlock.Length}"); + + Utils.Assert(envBlock.Length == continuationSizeInBytes, $"Continuation size must equal the reported size in the payload response. Expected: {continuationSizeInBytes} bytes, Received: {envBlock.Length} bytes"); + + // VALIDATE ENV + // env block is sent as Array (length-prefixed array of length-prefixed wchar strings) + int start = 0; + int end = start + 4 /* sizeof(uint32_t) */; + UInt32 envCount = BitConverter.ToUInt32(envBlock[start..end]); + Logger.logger.Log($"envCount: {envCount}"); + + var env = new Dictionary(); + for (int i = 0; i < envCount; i++) + { + start = end; + end = start + 4 /* sizeof(uint32_t) */; + UInt32 pairLength = BitConverter.ToUInt32(envBlock[start..end]); + + start = end; + end = start + ((int)pairLength * sizeof(char)); + Utils.Assert(end <= envBlock.Length, $"String end can't exceed payload size. Expected: <{envBlock.Length}, Received: {end} (decoded length: {pairLength})"); + string envPair = System.Text.Encoding.Unicode.GetString(envBlock[start..end]).TrimEnd('\0'); + int equalsIndex = envPair.IndexOf('='); + env[envPair[0..equalsIndex]] = envPair[(equalsIndex+1)..]; + } + Logger.logger.Log($"finished parsing env"); + + + foreach (var (key, val) in testEnvPairs) + Utils.Assert(env.ContainsKey(key) && env[key].Equals(val), $"Did not find test environment pair in the environment block: '{key}' = '{val}'"); + Logger.logger.Log($"Saw test values in env"); + + Utils.Assert(end == envBlock.Length, $"Full payload should have been read. Expected: {envBlock.Length}, Received: {end}"); + return Task.FromResult(true); + } + ); + + return subprocessTask.Result ? 100 : 0; } } } \ No newline at end of file