From 1306b036c1414afa39608ed8ede24fe003191132 Mon Sep 17 00:00:00 2001 From: Vitek Karas Date: Thu, 17 Jun 2021 22:21:55 +0200 Subject: [PATCH 001/107] Add a test for Expression.Property and its handling of property accessors (#54279) * Add a test for Expression.Property and its handling of property accessors This mostly a linker test as this effectively validates linker feature from https://github.com/mono/linker/pull/1880. But having a clean trimming tests here is better validation for this specific case. * Add one more test case as per feedback * Remove debugging leftover --- .../TrimmingTests/ExpressionPropertyTests.cs | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 src/libraries/System.Linq.Expressions/tests/TrimmingTests/ExpressionPropertyTests.cs diff --git a/src/libraries/System.Linq.Expressions/tests/TrimmingTests/ExpressionPropertyTests.cs b/src/libraries/System.Linq.Expressions/tests/TrimmingTests/ExpressionPropertyTests.cs new file mode 100644 index 0000000000000..0c038f0af75bf --- /dev/null +++ b/src/libraries/System.Linq.Expressions/tests/TrimmingTests/ExpressionPropertyTests.cs @@ -0,0 +1,77 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; + +/// +/// Tests that the System.Linq.Expressions.Expression.Property will correctly preserve both +/// accessors even if only one is referenced by MethodInfo. +/// +internal class Program +{ + static int Main(string[] args) + { + int result = ExplicitCreation.Test(); + if (result != 100) + return result; + + result = LambdaCreation.Test(); + + return result; + } + + class ExplicitCreation + { + class TestType + { + public bool _testPropertyValue; + + public bool TestProperty { + get => _testPropertyValue; + set { _testPropertyValue = value; } + } + } + + public static int Test() + { + var obj = new TestType(); + var param = Expression.Parameter(typeof(TestType)); + var prop = Expression.Property(param, typeof(TestType).GetMethod("get_TestProperty")); + ((PropertyInfo)prop.Member).SetValue(obj, true); + if (obj._testPropertyValue != true) + return -1; + + return 100; + } + } + + class LambdaCreation + { + class TestType + { + public bool _testPropertyValue; + + public bool TestProperty { + get => _testPropertyValue; + set { _testPropertyValue = value; } + } + } + + public static int Test() + { + var obj = new TestType (); + Expression> expression = t => t.TestProperty; + var prop = ((MemberExpression)expression.Body); + ((PropertyInfo)prop.Member).SetValue(obj, true); + + if (obj._testPropertyValue != true) + return -2; + + return 100; + } + } +} From 72a26f921ffbd49bb5a634d8ad2fa81f0a549928 Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Thu, 17 Jun 2021 14:13:47 -0700 Subject: [PATCH 002/107] Update superpmi_setup.py script with more exclusions (#54309) I noticed a bunch of failures trying to run crossgen2 on these binaries in one of our collections. Also, make the checking case-insensitive: I saw that we were failing to match "corerun.exe" against "CoreRun.exe" that was in the exclusion list. --- src/coreclr/scripts/superpmi_setup.py | 51 ++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/src/coreclr/scripts/superpmi_setup.py b/src/coreclr/scripts/superpmi_setup.py index e92991cf15ca3..7fd7d62b25600 100644 --- a/src/coreclr/scripts/superpmi_setup.py +++ b/src/coreclr/scripts/superpmi_setup.py @@ -59,6 +59,46 @@ is_windows = platform.system() == "Windows" native_binaries_to_ignore = [ + "api-ms-win-core-console-l1-1-0.dll", + "api-ms-win-core-datetime-l1-1-0.dll", + "api-ms-win-core-debug-l1-1-0.dll", + "api-ms-win-core-errorhandling-l1-1-0.dll", + "api-ms-win-core-file-l1-1-0.dll", + "api-ms-win-core-file-l1-2-0.dll", + "api-ms-win-core-file-l2-1-0.dll", + "api-ms-win-core-handle-l1-1-0.dll", + "api-ms-win-core-heap-l1-1-0.dll", + "api-ms-win-core-interlocked-l1-1-0.dll", + "api-ms-win-core-libraryloader-l1-1-0.dll", + "api-ms-win-core-localization-l1-2-0.dll", + "api-ms-win-core-memory-l1-1-0.dll", + "api-ms-win-core-namedpipe-l1-1-0.dll", + "api-ms-win-core-processenvironment-l1-1-0.dll", + "api-ms-win-core-processthreads-l1-1-0.dll", + "api-ms-win-core-processthreads-l1-1-1.dll", + "api-ms-win-core-profile-l1-1-0.dll", + "api-ms-win-core-rtlsupport-l1-1-0.dll", + "api-ms-win-core-string-l1-1-0.dll", + "api-ms-win-core-synch-l1-1-0.dll", + "api-ms-win-core-synch-l1-2-0.dll", + "api-ms-win-core-sysinfo-l1-1-0.dll", + "api-ms-win-core-timezone-l1-1-0.dll", + "api-ms-win-core-util-l1-1-0.dll", + "api-ms-win-crt-conio-l1-1-0.dll", + "api-ms-win-crt-convert-l1-1-0.dll", + "api-ms-win-crt-environment-l1-1-0.dll", + "api-ms-win-crt-filesystem-l1-1-0.dll", + "api-ms-win-crt-heap-l1-1-0.dll", + "api-ms-win-crt-locale-l1-1-0.dll", + "api-ms-win-crt-math-l1-1-0.dll", + "api-ms-win-crt-multibyte-l1-1-0.dll", + "api-ms-win-crt-private-l1-1-0.dll", + "api-ms-win-crt-process-l1-1-0.dll", + "api-ms-win-crt-runtime-l1-1-0.dll", + "api-ms-win-crt-stdio-l1-1-0.dll", + "api-ms-win-crt-string-l1-1-0.dll", + "api-ms-win-crt-time-l1-1-0.dll", + "api-ms-win-crt-utility-l1-1-0.dll", "clretwrc.dll", "clrgc.dll", "clrjit.dll", @@ -68,6 +108,9 @@ "clrjit_unix_arm_x86.dll", "clrjit_unix_arm64_arm64.dll", "clrjit_unix_arm64_x64.dll", + "clrjit_unix_armel_arm.dll", + "clrjit_unix_armel_arm64.dll", + "clrjit_unix_armel_x64.dll", "clrjit_unix_armel_x86.dll", "clrjit_unix_osx_arm64_arm64.dll", "clrjit_unix_osx_arm64_x64.dll", @@ -92,6 +135,7 @@ "CoreShim.dll", "createdump.exe", "crossgen.exe", + "crossgen2.exe", "dbgshim.dll", "ilasm.exe", "ildasm.exe", @@ -108,12 +152,15 @@ "mscordbi.dll", "mscorrc.dll", "msdia140.dll", + "R2RDump.exe", + "R2RTest.exe", "superpmi.exe", "superpmi-shim-collector.dll", "superpmi-shim-counter.dll", "superpmi-shim-simple.dll", "System.IO.Compression.Native.dll", "ucrtbase.dll", + "xunit.console.exe", ] MAX_FILES_COUNT = 1500 @@ -202,7 +249,9 @@ def sorter_by_size(pair): # Credit: https://stackoverflow.com/a/19859907 dirs[:] = [d for d in dirs if d not in exclude_directories] for name in files: - if name in exclude_files: + # Make the exclude check case-insensitive + exclude_files_lower = [filename.lower() for filename in exclude_files] + if name.lower() in exclude_files_lower: continue curr_file_path = path.join(file_path, name) From 64942392cb3f588f0d68a792010e9716283c5b5d Mon Sep 17 00:00:00 2001 From: Mateo Torres-Ruiz Date: Thu, 17 Jun 2021 15:09:27 -0700 Subject: [PATCH 003/107] Codesign apphosts on Mac (#53913) * Add CodeSign to HostWriter * Fix test * PR feedback * Add EnableMacOSCodeSign to CreateAppHost Add tests * Check that OSPlatform is OSX before running codesign. * Guard from filepaths containing spaces * Move apphost exceptions to a single file Modify apphost exceptions inheritance * Move AppHostUpdateException * Apply suggestions from code review Co-authored-by: Jan Kotas * Add exit code to AppHostSigningException Co-authored-by: Jan Kotas --- ...HostCustomizationUnsupportedOSException.cs | 15 --- .../AppHost/AppHostExceptions.cs | 92 +++++++++++++++ .../AppHost/AppHostNotCUIException.cs | 15 --- .../AppHost/AppHostNotPEFileException.cs | 15 --- .../AppHost/AppHostUpdateException.cs | 15 --- .../AppHost/AppNameTooLongException.cs | 20 ---- .../AppHost/HostWriter.cs | 30 ++++- ...FormatException.cs => MachOFormatError.cs} | 18 +-- .../AppHostUpdateTests.cs | 109 +++++++++++++++++- 9 files changed, 229 insertions(+), 100 deletions(-) delete mode 100644 src/installer/managed/Microsoft.NET.HostModel/AppHost/AppHostCustomizationUnsupportedOSException.cs create mode 100644 src/installer/managed/Microsoft.NET.HostModel/AppHost/AppHostExceptions.cs delete mode 100644 src/installer/managed/Microsoft.NET.HostModel/AppHost/AppHostNotCUIException.cs delete mode 100644 src/installer/managed/Microsoft.NET.HostModel/AppHost/AppHostNotPEFileException.cs delete mode 100644 src/installer/managed/Microsoft.NET.HostModel/AppHost/AppHostUpdateException.cs delete mode 100644 src/installer/managed/Microsoft.NET.HostModel/AppHost/AppNameTooLongException.cs rename src/installer/managed/Microsoft.NET.HostModel/AppHost/{AppHostMachOFormatException.cs => MachOFormatError.cs} (75%) diff --git a/src/installer/managed/Microsoft.NET.HostModel/AppHost/AppHostCustomizationUnsupportedOSException.cs b/src/installer/managed/Microsoft.NET.HostModel/AppHost/AppHostCustomizationUnsupportedOSException.cs deleted file mode 100644 index 0b0b055bdc688..0000000000000 --- a/src/installer/managed/Microsoft.NET.HostModel/AppHost/AppHostCustomizationUnsupportedOSException.cs +++ /dev/null @@ -1,15 +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; - -namespace Microsoft.NET.HostModel.AppHost -{ - /// - /// The application host executable cannot be customized because adding resources requires - /// that the build be performed on Windows (excluding Nano Server). - /// - public class AppHostCustomizationUnsupportedOSException : AppHostUpdateException - { - } -} diff --git a/src/installer/managed/Microsoft.NET.HostModel/AppHost/AppHostExceptions.cs b/src/installer/managed/Microsoft.NET.HostModel/AppHost/AppHostExceptions.cs new file mode 100644 index 0000000000000..ceae7c46cdc80 --- /dev/null +++ b/src/installer/managed/Microsoft.NET.HostModel/AppHost/AppHostExceptions.cs @@ -0,0 +1,92 @@ +// 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.AppHost +{ + /// + /// An instance of this exception is thrown when an AppHost binary update + /// fails due to known user errors. + /// + public class AppHostUpdateException : Exception + { + internal AppHostUpdateException(string message = null) + : base(message) + { + } + } + + /// + /// The application host executable cannot be customized because adding resources requires + /// that the build be performed on Windows (excluding Nano Server). + /// + public sealed class AppHostCustomizationUnsupportedOSException : AppHostUpdateException + { + internal AppHostCustomizationUnsupportedOSException() + { + } + } + + /// + /// The MachO application host executable cannot be customized because + /// it was not in the expected format + /// + public sealed class AppHostMachOFormatException : AppHostUpdateException + { + public readonly MachOFormatError Error; + + internal AppHostMachOFormatException(MachOFormatError error) + { + Error = error; + } + } + + /// + /// Unable to use the input file as application host executable because it's not a + /// Windows executable for the CUI (Console) subsystem. + /// + public sealed class AppHostNotCUIException : AppHostUpdateException + { + internal AppHostNotCUIException() + { + } + } + + /// + /// Unable to use the input file as an application host executable + /// because it's not a Windows PE file + /// + public sealed class AppHostNotPEFileException : AppHostUpdateException + { + internal AppHostNotPEFileException() + { + } + } + + /// + /// Unable to sign the apphost binary. + /// + public sealed class AppHostSigningException : AppHostUpdateException + { + public readonly int ExitCode; + + internal AppHostSigningException(int exitCode, string signingErrorMessage) + : base(signingErrorMessage) + { + } + } + + /// + /// Given app file name is longer than 1024 bytes + /// + public sealed class AppNameTooLongException : AppHostUpdateException + { + public string LongName { get; } + + internal AppNameTooLongException(string name) + { + LongName = name; + } + } +} diff --git a/src/installer/managed/Microsoft.NET.HostModel/AppHost/AppHostNotCUIException.cs b/src/installer/managed/Microsoft.NET.HostModel/AppHost/AppHostNotCUIException.cs deleted file mode 100644 index b89a22dced400..0000000000000 --- a/src/installer/managed/Microsoft.NET.HostModel/AppHost/AppHostNotCUIException.cs +++ /dev/null @@ -1,15 +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; - -namespace Microsoft.NET.HostModel.AppHost -{ - /// - /// Unable to use the input file as application host executable because it's not a - /// Windows executable for the CUI (Console) subsystem. - /// - public class AppHostNotCUIException : AppHostUpdateException - { - } -} diff --git a/src/installer/managed/Microsoft.NET.HostModel/AppHost/AppHostNotPEFileException.cs b/src/installer/managed/Microsoft.NET.HostModel/AppHost/AppHostNotPEFileException.cs deleted file mode 100644 index 6712d875f7683..0000000000000 --- a/src/installer/managed/Microsoft.NET.HostModel/AppHost/AppHostNotPEFileException.cs +++ /dev/null @@ -1,15 +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; - -namespace Microsoft.NET.HostModel.AppHost -{ - /// - /// Unable to use the input file as an application host executable - /// because it's not a Windows PE file - /// - public class AppHostNotPEFileException : AppHostUpdateException - { - } -} diff --git a/src/installer/managed/Microsoft.NET.HostModel/AppHost/AppHostUpdateException.cs b/src/installer/managed/Microsoft.NET.HostModel/AppHost/AppHostUpdateException.cs deleted file mode 100644 index 4570c7710c2db..0000000000000 --- a/src/installer/managed/Microsoft.NET.HostModel/AppHost/AppHostUpdateException.cs +++ /dev/null @@ -1,15 +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; - -namespace Microsoft.NET.HostModel.AppHost -{ - /// - /// An instance of this exception is thrown when an AppHost binary update - /// fails due to known user errors. - /// - public class AppHostUpdateException : Exception - { - } -} diff --git a/src/installer/managed/Microsoft.NET.HostModel/AppHost/AppNameTooLongException.cs b/src/installer/managed/Microsoft.NET.HostModel/AppHost/AppNameTooLongException.cs deleted file mode 100644 index 18a984b17c591..0000000000000 --- a/src/installer/managed/Microsoft.NET.HostModel/AppHost/AppNameTooLongException.cs +++ /dev/null @@ -1,20 +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; - -namespace Microsoft.NET.HostModel.AppHost -{ - /// - /// Given app file name is longer than 1024 bytes - /// - public class AppNameTooLongException : AppHostUpdateException - { - public string LongName { get; } - public AppNameTooLongException(string name) - { - LongName = name; - } - - } -} diff --git a/src/installer/managed/Microsoft.NET.HostModel/AppHost/HostWriter.cs b/src/installer/managed/Microsoft.NET.HostModel/AppHost/HostWriter.cs index 69ad0d439bd64..c0e979dd28d27 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/AppHost/HostWriter.cs +++ b/src/installer/managed/Microsoft.NET.HostModel/AppHost/HostWriter.cs @@ -3,6 +3,7 @@ using System; using System.ComponentModel; +using System.Diagnostics; using System.IO; using System.IO.MemoryMappedFiles; using System.Runtime.InteropServices; @@ -30,12 +31,14 @@ public static class HostWriter /// Full path to app binary or relative path to the result apphost file /// Specify whether to set the subsystem to GUI. Only valid for PE apphosts. /// Path to the intermediate assembly, used for copying resources to PE apphosts. + /// Sign the app binary using codesign with an anonymous certificate. public static void CreateAppHost( string appHostSourceFilePath, string appHostDestinationFilePath, string appBinaryFilePath, bool windowsGraphicalUserInterface = false, - string assemblyToCopyResorcesFrom = null) + string assemblyToCopyResorcesFrom = null, + bool enableMacOSCodeSign = false) { var bytesToWrite = Encoding.UTF8.GetBytes(appBinaryFilePath); if (bytesToWrite.Length > 1024) @@ -140,6 +143,9 @@ void UpdateResources() { throw new Win32Exception(Marshal.GetLastWin32Error(), $"Could not set file permission {filePermissionOctal} for {appHostDestinationFilePath}."); } + + if (enableMacOSCodeSign && RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + CodeSign(appHostDestinationFilePath); } } catch (Exception ex) @@ -233,6 +239,28 @@ void FindBundleHeader() return headerOffset != 0; } + private static void CodeSign(string appHostPath) + { + Debug.Assert(RuntimeInformation.IsOSPlatform(OSPlatform.OSX)); + const string codesign = @"/usr/bin/codesign"; + if (!File.Exists(codesign)) + return; + + var psi = new ProcessStartInfo() + { + Arguments = $"-s - \"{appHostPath}\"", + FileName = codesign, + RedirectStandardError = true, + }; + + using (var p = Process.Start(psi)) + { + p.WaitForExit(); + if (p.ExitCode != 0) + throw new AppHostSigningException(p.ExitCode, p.StandardError.ReadToEnd()); + } + } + [DllImport("libc", SetLastError = true)] private static extern int chmod(string pathname, int mode); } diff --git a/src/installer/managed/Microsoft.NET.HostModel/AppHost/AppHostMachOFormatException.cs b/src/installer/managed/Microsoft.NET.HostModel/AppHost/MachOFormatError.cs similarity index 75% rename from src/installer/managed/Microsoft.NET.HostModel/AppHost/AppHostMachOFormatException.cs rename to src/installer/managed/Microsoft.NET.HostModel/AppHost/MachOFormatError.cs index 60e6d74ec591e..eae935f13af27 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/AppHost/AppHostMachOFormatException.cs +++ b/src/installer/managed/Microsoft.NET.HostModel/AppHost/MachOFormatError.cs @@ -1,8 +1,6 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; - namespace Microsoft.NET.HostModel.AppHost { /// @@ -25,18 +23,4 @@ public enum MachOFormatError InvalidUTF8, // UTF8 decoding failed SignNotRemoved, // Signature not removed from the host (while processing a single-file bundle) } - - /// - /// The MachO application host executable cannot be customized because - /// it was not in the expected format - /// - public class AppHostMachOFormatException : AppHostUpdateException - { - public readonly MachOFormatError Error; - - public AppHostMachOFormatException(MachOFormatError error) - { - Error = error; - } - } } diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.AppHost.Tests/AppHostUpdateTests.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.AppHost.Tests/AppHostUpdateTests.cs index fc0aedbdfbfd7..3aa8886babe37 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.AppHost.Tests/AppHostUpdateTests.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.AppHost.Tests/AppHostUpdateTests.cs @@ -12,6 +12,7 @@ using Xunit; using Microsoft.NET.HostModel.AppHost; using Microsoft.DotNet.CoreSetup.Test; +using System.Diagnostics; namespace Microsoft.NET.HostModel.Tests { @@ -220,6 +221,109 @@ public void CanCreateAppHost() } } + [Theory] + [PlatformSpecific(TestPlatforms.OSX)] + [InlineData("")] + [InlineData("dir with spaces")] + public void CanCodeSignAppHostOnMacOS(string subdir) + { + using (TestDirectory testDirectory = TestDirectory.Create(subdir)) + { + string sourceAppHostMock = PrepareAppHostMockFile(testDirectory); + File.SetAttributes(sourceAppHostMock, FileAttributes.ReadOnly); + string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock"); + string appBinaryFilePath = "Test/App/Binary/Path.dll"; + HostWriter.CreateAppHost( + sourceAppHostMock, + destinationFilePath, + appBinaryFilePath, + windowsGraphicalUserInterface: false, + enableMacOSCodeSign: true); + + const string codesign = @"/usr/bin/codesign"; + var psi = new ProcessStartInfo() + { + Arguments = $"-d \"{destinationFilePath}\"", + FileName = codesign, + RedirectStandardError = true, + }; + + using (var p = Process.Start(psi)) + { + p.Start(); + p.StandardError.ReadToEnd() + .Should().Contain($"Executable=/private{Path.GetFullPath(destinationFilePath)}"); + p.WaitForExit(); + // Successfully signed the apphost. + Assert.True(p.ExitCode == 0, $"Expected exit code was '0' but '{codesign}' returned '{p.ExitCode}' instead."); + } + } + } + + [Fact] + [PlatformSpecific(TestPlatforms.OSX)] + public void ItDoesNotCodeSignAppHostByDefault() + { + using (TestDirectory testDirectory = TestDirectory.Create()) + { + string sourceAppHostMock = PrepareAppHostMockFile(testDirectory); + File.SetAttributes(sourceAppHostMock, FileAttributes.ReadOnly); + string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock"); + string appBinaryFilePath = "Test/App/Binary/Path.dll"; + HostWriter.CreateAppHost( + sourceAppHostMock, + destinationFilePath, + appBinaryFilePath, + windowsGraphicalUserInterface: false); + + const string codesign = @"/usr/bin/codesign"; + var psi = new ProcessStartInfo() + { + Arguments = $"-d {destinationFilePath}", + FileName = codesign, + RedirectStandardError = true, + }; + + using (var p = Process.Start(psi)) + { + p.Start(); + p.StandardError.ReadToEnd() + .Should().Contain($"{Path.GetFullPath(destinationFilePath)}: code object is not signed at all"); + p.WaitForExit(); + } + } + } + + [Fact] + [PlatformSpecific(TestPlatforms.OSX)] + public void CodeSigningFailuresThrow() + { + using (TestDirectory testDirectory = TestDirectory.Create()) + { + string sourceAppHostMock = PrepareAppHostMockFile(testDirectory); + File.SetAttributes(sourceAppHostMock, FileAttributes.ReadOnly); + string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock"); + string appBinaryFilePath = "Test/App/Binary/Path.dll"; + HostWriter.CreateAppHost( + sourceAppHostMock, + destinationFilePath, + appBinaryFilePath, + windowsGraphicalUserInterface: false, + enableMacOSCodeSign: true); + + // Run CreateAppHost again to sign the apphost a second time, + // causing codesign to fail. + var exception = Assert.Throws(() => + HostWriter.CreateAppHost( + sourceAppHostMock, + destinationFilePath, + appBinaryFilePath, + windowsGraphicalUserInterface: false, + enableMacOSCodeSign: true)); + Assert.Contains($"{destinationFilePath}: is already signed", exception.Message); + } + } + private string PrepareAppHostMockFile(TestDirectory testDirectory, Action customize = null) { // For now we're testing the AppHost on Windows PE files only. @@ -334,11 +438,12 @@ private TestDirectory(string path) Directory.CreateDirectory(path); } - public static TestDirectory Create([CallerMemberName] string callingMethod = "") + public static TestDirectory Create([CallerMemberName] string callingMethod = "", string subDir = "") { string path = System.IO.Path.Combine( System.IO.Path.GetTempPath(), - "dotNetSdkUnitTest_" + callingMethod + (Guid.NewGuid().ToString().Substring(0, 8))); + "dotNetSdkUnitTest_" + callingMethod + (Guid.NewGuid().ToString().Substring(0, 8)), + subDir); return new TestDirectory(path); } From 25aacfdafb151e41d8c0b6185029c144fd66dbbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B8is=C3=A6ther=20Rasch?= Date: Fri, 18 Jun 2021 00:15:45 +0200 Subject: [PATCH 004/107] Ms.Ext.Options.OptionsBuilder.BindConfiguration adds ConfigurationChangeTokenSource to DI (#46740) --- .../OptionsBuilderConfigurationExtensions.cs | 1 + .../tests/FakeConfigurationProvider.cs | 20 +++++ .../tests/FakeConfigurationSource.cs | 19 ++++ ...ionsBuidlerConfigurationExtensionsTests.cs | 88 +++++++++++++++++-- 4 files changed, 120 insertions(+), 8 deletions(-) create mode 100644 src/libraries/Microsoft.Extensions.Options.ConfigurationExtensions/tests/FakeConfigurationProvider.cs create mode 100644 src/libraries/Microsoft.Extensions.Options.ConfigurationExtensions/tests/FakeConfigurationSource.cs diff --git a/src/libraries/Microsoft.Extensions.Options.ConfigurationExtensions/src/OptionsBuilderConfigurationExtensions.cs b/src/libraries/Microsoft.Extensions.Options.ConfigurationExtensions/src/OptionsBuilderConfigurationExtensions.cs index 3025442fb3099..128f0eda38aa4 100644 --- a/src/libraries/Microsoft.Extensions.Options.ConfigurationExtensions/src/OptionsBuilderConfigurationExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Options.ConfigurationExtensions/src/OptionsBuilderConfigurationExtensions.cs @@ -70,6 +70,7 @@ public static class OptionsBuilderConfigurationExtensions _ = configSectionPath ?? throw new ArgumentNullException(nameof(configSectionPath)); optionsBuilder.Configure((opts, config) => BindFromOptions(opts, config, configSectionPath, configureBinder)); + optionsBuilder.Services.AddSingleton, ConfigurationChangeTokenSource>(); return optionsBuilder; } diff --git a/src/libraries/Microsoft.Extensions.Options.ConfigurationExtensions/tests/FakeConfigurationProvider.cs b/src/libraries/Microsoft.Extensions.Options.ConfigurationExtensions/tests/FakeConfigurationProvider.cs new file mode 100644 index 0000000000000..62f25a8d5862a --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Options.ConfigurationExtensions/tests/FakeConfigurationProvider.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration.Memory; + +namespace Microsoft.Extensions.Options.ConfigurationExtensions.Tests +{ + internal class FakeConfigurationProvider : MemoryConfigurationProvider, IConfigurationProvider + { + public FakeConfigurationProvider(MemoryConfigurationSource source) + : base(source) { } + + public new void Set(string key, string value) + { + base.Set(key, value); + OnReload(); + } + } +} diff --git a/src/libraries/Microsoft.Extensions.Options.ConfigurationExtensions/tests/FakeConfigurationSource.cs b/src/libraries/Microsoft.Extensions.Options.ConfigurationExtensions/tests/FakeConfigurationSource.cs new file mode 100644 index 0000000000000..512061a3ea75e --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Options.ConfigurationExtensions/tests/FakeConfigurationSource.cs @@ -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. + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration.Memory; + +namespace Microsoft.Extensions.Options.ConfigurationExtensions.Tests +{ + internal class FakeConfigurationSource : MemoryConfigurationSource, IConfigurationSource + { + internal IConfigurationProvider Provider { get; private set; } = null!; + + public new IConfigurationProvider Build(IConfigurationBuilder builder) + { + Provider = new FakeConfigurationProvider(this); + return Provider; + } + } +} diff --git a/src/libraries/Microsoft.Extensions.Options.ConfigurationExtensions/tests/OptionsBuidlerConfigurationExtensionsTests.cs b/src/libraries/Microsoft.Extensions.Options.ConfigurationExtensions/tests/OptionsBuidlerConfigurationExtensionsTests.cs index 0aa34a9650f4a..9d4764c19c0b4 100644 --- a/src/libraries/Microsoft.Extensions.Options.ConfigurationExtensions/tests/OptionsBuidlerConfigurationExtensionsTests.cs +++ b/src/libraries/Microsoft.Extensions.Options.ConfigurationExtensions/tests/OptionsBuidlerConfigurationExtensionsTests.cs @@ -26,7 +26,7 @@ public static void BindConfiguration_ThrowsForNullBuilder() public static void BindConfiguration_ThrowsForNullConfigurationSectionPath() { var services = new ServiceCollection(); - var optionsBuilder = new OptionsBuilder(services, Options.DefaultName); + OptionsBuilder optionsBuilder = new(services, Options.DefaultName); string configSectionPath = null!; Assert.Throws("configSectionPath", () => @@ -39,9 +39,9 @@ public static void BindConfiguration_ThrowsForNullConfigurationSectionPath() public static void BindConfiguration_ReturnsSameBuilderInstance() { var services = new ServiceCollection(); - var optionsBuilder = new OptionsBuilder(services, Options.DefaultName); + OptionsBuilder optionsBuilder = new(services, Options.DefaultName); - var returnedBuilder = optionsBuilder.BindConfiguration("Test"); + OptionsBuilder returnedBuilder = optionsBuilder.BindConfiguration("Test"); Assert.Same(optionsBuilder, returnedBuilder); } @@ -50,7 +50,7 @@ public static void BindConfiguration_ReturnsSameBuilderInstance() public static void BindConfiguration_OptionsMaterializationThrowsIfNoConfigurationInDI() { var services = new ServiceCollection(); - var optionsBuilder = services.AddOptions(); + OptionsBuilder optionsBuilder = services.AddOptions(); _ = optionsBuilder.BindConfiguration("Test"); using ServiceProvider serviceProvider = services.BuildServiceProvider(); @@ -74,12 +74,12 @@ public static void BindConfiguration_UsesConfigurationSectionPath() services.AddSingleton(new ConfigurationBuilder() .AddInMemoryCollection(configEntries) .Build()); - var optionsBuilder = services.AddOptions(); + OptionsBuilder optionsBuilder = services.AddOptions(); _ = optionsBuilder.BindConfiguration(configSectionName); using ServiceProvider serviceProvider = services.BuildServiceProvider(); - var options = serviceProvider.GetRequiredService>().Value; + FakeOptions options = serviceProvider.GetRequiredService>().Value; Assert.Equal(messageValue, options.Message); } @@ -96,14 +96,86 @@ public static void BindConfiguration_UsesConfigurationRootIfSectionNameIsEmptySt services.AddSingleton(new ConfigurationBuilder() .AddInMemoryCollection(configEntries) .Build()); - var optionsBuilder = services.AddOptions(); + OptionsBuilder optionsBuilder = services.AddOptions(); _ = optionsBuilder.BindConfiguration(configSectionPath: ""); using ServiceProvider serviceProvider = services.BuildServiceProvider(); - var options = serviceProvider.GetRequiredService>().Value; + FakeOptions options = serviceProvider.GetRequiredService>().Value; Assert.Equal(messageValue, options.Message); } + + [Fact] + public static void BindConfiguration_UpdatesOptionOnConfigurationUpdateWithEmptySectionName() + { + const string messageValue1 = "This is a test"; + const string messageValue2 = "This is the message after update"; + + FakeConfigurationSource configSource = new() + { + InitialData = new Dictionary + { + [nameof(FakeOptions.Message)] = messageValue1, + } + }; + + var services = new ServiceCollection(); + services.AddSingleton(new ConfigurationBuilder() + .Add(configSource) + .Build()); + OptionsBuilder optionsBuilder = services.AddOptions(); + _ = optionsBuilder.BindConfiguration(configSectionPath: ""); + using ServiceProvider serviceProvider = services.BuildServiceProvider(); + var optionsMonitor = serviceProvider.GetRequiredService>(); + bool updateHasRun = false; + optionsMonitor.OnChange((opts, name) => + { + updateHasRun = true; + }); + FakeOptions optionsValue1 = optionsMonitor.CurrentValue; + Assert.Equal(messageValue1, optionsValue1.Message); + configSource.Provider.Set(nameof(FakeOptions.Message), messageValue2); + FakeOptions optionsValue2 = optionsMonitor.CurrentValue; + Assert.True(updateHasRun); + Assert.Equal(messageValue2, optionsValue2.Message); + } + + [Fact] + public static void BindConfiguration_UpdatesOptionOnConfigurationUpdate() + { + const string configSectionName = "Test"; + string messageConfigKey = ConfigurationPath.Combine(configSectionName, nameof(FakeOptions.Message)); + const string messageValue1 = "This is a test"; + const string messageValue2 = "This is the message after update"; + + FakeConfigurationSource configSource = new() + { + InitialData = new Dictionary + { + [messageConfigKey] = messageValue1 + } + }; + + var services = new ServiceCollection(); + services.AddSingleton(new ConfigurationBuilder() + .Add(configSource) + .Build()); + OptionsBuilder optionsBuilder = services.AddOptions(); + _ = optionsBuilder.BindConfiguration(configSectionName); + using ServiceProvider serviceProvider = services.BuildServiceProvider(); + var optionsMonitor = serviceProvider.GetRequiredService>(); + bool updateHasRun = false; + optionsMonitor.OnChange((opts, name) => + { + updateHasRun = true; + }); + FakeOptions optionsValue1 = optionsMonitor.CurrentValue; + Assert.Equal(messageValue1, optionsValue1.Message); + configSource.Provider.Set(messageConfigKey, messageValue2); + FakeOptions optionsValue2 = optionsMonitor.CurrentValue; + Assert.True(updateHasRun); + Assert.Equal(messageValue2, optionsValue2.Message); + } } } From 034986bfaf55a7177745a379b0ae77e1a7998545 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Thu, 17 Jun 2021 19:57:43 -0400 Subject: [PATCH 005/107] Fix lots of unnecessary XmlValueGetter delegate allocations (#54344) --- .../src/System/Xml/Core/XsdValidatingReader.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XsdValidatingReader.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XsdValidatingReader.cs index 294b174638b79..a42e0fef36c26 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XsdValidatingReader.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XsdValidatingReader.cs @@ -2015,12 +2015,12 @@ private void ProcessReaderEvent() case XmlNodeType.Whitespace: case XmlNodeType.SignificantWhitespace: - _validator.ValidateWhitespace(GetStringValue); + _validator.ValidateWhitespace(_valueGetter); break; case XmlNodeType.Text: // text inside a node case XmlNodeType.CDATA: // - _validator.ValidateText(GetStringValue); + _validator.ValidateText(_valueGetter); break; case XmlNodeType.EndElement: @@ -2543,12 +2543,12 @@ private object InternalReadContentAsObject(bool unwrapTypedValue, out string ori case XmlNodeType.Text: case XmlNodeType.CDATA: - _validator.ValidateText(GetStringValue); + _validator.ValidateText(_valueGetter); break; case XmlNodeType.Whitespace: case XmlNodeType.SignificantWhitespace: - _validator.ValidateWhitespace(GetStringValue); + _validator.ValidateWhitespace(_valueGetter); break; case XmlNodeType.Comment: @@ -2611,12 +2611,12 @@ private void ReadAheadForMemberType() case XmlNodeType.Text: case XmlNodeType.CDATA: - _validator.ValidateText(GetStringValue); + _validator.ValidateText(_valueGetter); break; case XmlNodeType.Whitespace: case XmlNodeType.SignificantWhitespace: - _validator.ValidateWhitespace(GetStringValue); + _validator.ValidateWhitespace(_valueGetter); break; case XmlNodeType.Comment: @@ -2672,12 +2672,12 @@ private void GetIsDefault() case XmlNodeType.Text: case XmlNodeType.CDATA: - _validator.ValidateText(GetStringValue); + _validator.ValidateText(_valueGetter); break; case XmlNodeType.Whitespace: case XmlNodeType.SignificantWhitespace: - _validator.ValidateWhitespace(GetStringValue); + _validator.ValidateWhitespace(_valueGetter); break; case XmlNodeType.Comment: From 61e250a18970b5587f229388441f12418b1d58bf Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Thu, 17 Jun 2021 19:58:30 -0400 Subject: [PATCH 006/107] Lazy-allocate ValidationState.CurPos (#54346) The array is only used for a subset of validators; no point in paying for this array for every ValidationState instance even if it's not going to be used. --- .../src/System/Xml/Schema/ValidationState.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Schema/ValidationState.cs b/src/libraries/System.Private.Xml/src/System/Xml/Schema/ValidationState.cs index c7860253a5858..aa60c108b5c90 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Schema/ValidationState.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Schema/ValidationState.cs @@ -42,7 +42,8 @@ internal sealed class ValidationState public bool HasMatched; // whether the element has been verified correctly //For NFAs - public BitSet[] CurPos = new BitSet[2]; + private BitSet[]? _curPos; + public BitSet[] CurPos => _curPos ??= new BitSet[2]; //For all public BitSet? AllElementsSet; From 98be10a336f216127470214fedf928ba68635c60 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Thu, 17 Jun 2021 19:25:44 -0500 Subject: [PATCH 007/107] Only include Microsoft.NET.Runtime.RuntimeConfigParser.Task on mobile (#54361) * Only include Microsoft.NET.Runtime.RuntimeConfigParser.Task on mobile * Add maccatalyst target imports per request --- .../WorkloadManifest.targets | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets index 6e885124174bd..0c25bb7a938fa 100644 --- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets +++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets @@ -8,10 +8,9 @@ true - - + @@ -20,19 +19,29 @@ + + + + + + + + + + From d764cb5e228c707e40cf56713a64f6ce157afbff Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Thu, 17 Jun 2021 20:23:29 -0500 Subject: [PATCH 008/107] Fix casing (#54384) --- .../WorkloadManifest.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets index 0c25bb7a938fa..0e454c9bd5013 100644 --- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets +++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets @@ -50,6 +50,6 @@ - + From f721cf4144005334827c02931317366f7f6ee114 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Thu, 17 Jun 2021 19:06:29 -0700 Subject: [PATCH 009/107] Implement NativeMemory (#54006) * Implement NativeMemory * Exposing additional APIs as approved * Ensure we have a test covering alignment and size being less than sizeof(void*) * Update src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeMemory.Unix.cs Co-authored-by: Jan Kotas * Responding to PR feedback * Adding additional alignment test coverage for 1 to 16384 * Add coverage for 65k and 1/2/4MB alignments * Fixing the Native\Unix\System.Native\CMakeLists.txt * Update src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeMemory.Unix.cs Co-authored-by: Jan Kotas * Don't call Buffer.Memmove in NativeMemory.AlignedRealloc if ptr is null * Updating NativeMemory.AlignedRealloc to correctly copy only the size of the last allocation * Ensure check_symbol_exists(HAVE_ALIGNED_ALLOC) is under the non-apple paths * Check for malloc_usable_size in malloc_np for FreeBSD and ensure tests compile * Fix the ReallocSmallerToLargerTest test * Handle that posix_memalign differs from aligned_alloc for size == 0 Co-authored-by: Jan Kotas --- THIRD-PARTY-NOTICES.TXT | 26 ++ .../Unix/System.Native/Interop.MemAlloc.cs | 26 +- .../src/Interop/Windows/Interop.Libraries.cs | 1 + .../Windows/Ucrtbase/Interop.MemAlloc.cs | 32 ++ .../Native/Unix/Common/pal_config.h.in | 5 + .../Native/Unix/System.Native/entrypoints.c | 11 +- .../Native/Unix/System.Native/pal_memory.c | 75 ++- .../Native/Unix/System.Native/pal_memory.h | 35 +- src/libraries/Native/Unix/configure.cmake | 32 +- .../src/Resources/Strings.resx | 5 +- .../System.Private.CoreLib.Shared.projitems | 6 + .../src/System/Numerics/BitOperations.cs | 14 +- .../Runtime/InteropServices/Marshal.Unix.cs | 86 ++-- .../InteropServices/NativeMemory.Unix.cs | 210 +++++++++ .../InteropServices/NativeMemory.Windows.cs | 175 +++++++ .../Runtime/InteropServices/NativeMemory.cs | 53 +++ .../src/System/ThrowHelper.cs | 3 + .../ref/System.Runtime.InteropServices.cs | 21 + ...ystem.Runtime.InteropServices.Tests.csproj | 1 + .../InteropServices/NativeMemoryTests.cs | 439 ++++++++++++++++++ 20 files changed, 1175 insertions(+), 81 deletions(-) create mode 100644 src/libraries/Common/src/Interop/Windows/Ucrtbase/Interop.MemAlloc.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeMemory.Unix.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeMemory.Windows.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeMemory.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/NativeMemoryTests.cs diff --git a/THIRD-PARTY-NOTICES.TXT b/THIRD-PARTY-NOTICES.TXT index b0694275b3dbb..a877e8fb7abfb 100644 --- a/THIRD-PARTY-NOTICES.TXT +++ b/THIRD-PARTY-NOTICES.TXT @@ -952,3 +952,29 @@ by constants, including codegen instructions. The unsigned division incorporates "round down" optimization per ridiculous_fish. This is free and unencumbered software. Any copyright is dedicated to the Public Domain. + + +License notice for mimalloc +----------------------------------- + +MIT License + +Copyright (c) 2019 Microsoft Corporation, Daan Leijen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MemAlloc.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MemAlloc.cs index c5f6d1baa2adb..cb4a38ff4ea0f 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MemAlloc.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MemAlloc.cs @@ -6,15 +6,27 @@ internal static partial class Interop { - internal static partial class Sys + internal static unsafe partial class Sys { - [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_MemAlloc")] - internal static extern IntPtr MemAlloc(nuint sizeInBytes); + [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_AlignedAlloc")] + internal static extern void* AlignedAlloc(nuint alignment, nuint size); - [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_MemReAlloc")] - internal static extern IntPtr MemReAlloc(IntPtr ptr, nuint newSize); + [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_AlignedFree")] + internal static extern void AlignedFree(void* ptr); - [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_MemFree")] - internal static extern void MemFree(IntPtr ptr); + [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_AlignedRealloc")] + internal static extern void* AlignedRealloc(void* ptr, nuint alignment, nuint new_size); + + [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_Calloc")] + internal static extern void* Calloc(nuint num, nuint size); + + [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_Free")] + internal static extern void Free(void* ptr); + + [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_Malloc")] + internal static extern void* Malloc(nuint size); + + [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_Realloc")] + internal static extern void* Realloc(void* ptr, nuint new_size); } } diff --git a/src/libraries/Common/src/Interop/Windows/Interop.Libraries.cs b/src/libraries/Common/src/Interop/Windows/Interop.Libraries.cs index d3466d74a12ed..93c2bb72a806c 100644 --- a/src/libraries/Common/src/Interop/Windows/Interop.Libraries.cs +++ b/src/libraries/Common/src/Interop/Windows/Interop.Libraries.cs @@ -44,5 +44,6 @@ internal static partial class Libraries internal const string GlobalizationNative = "System.Globalization.Native"; internal const string MsQuic = "msquic.dll"; internal const string HostPolicy = "hostpolicy.dll"; + internal const string Ucrtbase = "ucrtbase.dll"; } } diff --git a/src/libraries/Common/src/Interop/Windows/Ucrtbase/Interop.MemAlloc.cs b/src/libraries/Common/src/Interop/Windows/Ucrtbase/Interop.MemAlloc.cs new file mode 100644 index 0000000000000..23fe143abad30 --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Ucrtbase/Interop.MemAlloc.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static unsafe partial class Ucrtbase + { + [DllImport(Libraries.Ucrtbase, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + internal static extern void* _aligned_malloc(nuint size, nuint alignment); + + [DllImport(Libraries.Ucrtbase, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + internal static extern void _aligned_free(void* ptr); + + [DllImport(Libraries.Ucrtbase, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + internal static extern void* _aligned_realloc(void* ptr, nuint size, nuint alignment); + + [DllImport(Libraries.Ucrtbase, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + internal static extern void* calloc(nuint num, nuint size); + + [DllImport(Libraries.Ucrtbase, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + internal static extern void free(void* ptr); + + [DllImport(Libraries.Ucrtbase, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + internal static extern void* malloc(nuint size); + + [DllImport(Libraries.Ucrtbase, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + internal static extern void* realloc(void* ptr, nuint new_size); + } +} diff --git a/src/libraries/Native/Unix/Common/pal_config.h.in b/src/libraries/Native/Unix/Common/pal_config.h.in index 034d57b1e1c98..2b86ed15d4cae 100644 --- a/src/libraries/Native/Unix/Common/pal_config.h.in +++ b/src/libraries/Native/Unix/Common/pal_config.h.in @@ -122,6 +122,11 @@ #cmakedefine01 HAVE_GETGROUPLIST #cmakedefine01 HAVE_SYS_PROCINFO_H #cmakedefine01 HAVE_IOSS_H +#cmakedefine01 HAVE_ALIGNED_ALLOC +#cmakedefine01 HAVE_MALLOC_SIZE +#cmakedefine01 HAVE_MALLOC_USABLE_SIZE +#cmakedefine01 HAVE_MALLOC_USABLE_SIZE_NP +#cmakedefine01 HAVE_POSIX_MEMALIGN // Mac OS X has stat64, but it is deprecated since plain stat now // provides the same 64-bit aware struct when targeting OS X > 10.5 diff --git a/src/libraries/Native/Unix/System.Native/entrypoints.c b/src/libraries/Native/Unix/System.Native/entrypoints.c index 8a1438b6c3d18..cf6485ecf8430 100644 --- a/src/libraries/Native/Unix/System.Native/entrypoints.c +++ b/src/libraries/Native/Unix/System.Native/entrypoints.c @@ -108,10 +108,15 @@ static const Entry s_sysNative[] = DllImportEntry(SystemNative_LChflagsCanSetHiddenFlag) DllImportEntry(SystemNative_ReadProcessStatusInfo) DllImportEntry(SystemNative_Log) - DllImportEntry(SystemNative_MemAlloc) - DllImportEntry(SystemNative_MemReAlloc) - DllImportEntry(SystemNative_MemFree) + DllImportEntry(SystemNative_AlignedAlloc) + DllImportEntry(SystemNative_AlignedFree) + DllImportEntry(SystemNative_AlignedRealloc) + DllImportEntry(SystemNative_Calloc) + DllImportEntry(SystemNative_Free) + DllImportEntry(SystemNative_GetUsableSize) + DllImportEntry(SystemNative_Malloc) DllImportEntry(SystemNative_MemSet) + DllImportEntry(SystemNative_Realloc) DllImportEntry(SystemNative_GetSpaceInfoForMountPoint) DllImportEntry(SystemNative_GetFormatInfoForMountPoint) DllImportEntry(SystemNative_GetAllMountPoints) diff --git a/src/libraries/Native/Unix/System.Native/pal_memory.c b/src/libraries/Native/Unix/System.Native/pal_memory.c index 35757dff03b9d..fd6a34de2c39a 100644 --- a/src/libraries/Native/Unix/System.Native/pal_memory.c +++ b/src/libraries/Native/Unix/System.Native/pal_memory.c @@ -1,27 +1,92 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#include "pal_config.h" #include "pal_memory.h" +#include #include #include -void* SystemNative_MemAlloc(uintptr_t size) +#if HAVE_MALLOC_SIZE + #include +#elif HAVE_MALLOC_USABLE_SIZE + #include +#elif HAVE_MALLOC_USABLE_SIZE_NP + #include +#else + #error "Platform doesn't support malloc_usable_size or malloc_size" +#endif + +void* SystemNative_AlignedAlloc(uintptr_t alignment, uintptr_t size) { - return malloc(size); +#if HAVE_ALIGNED_ALLOC + // We want to prefer the standardized aligned_alloc function. However + // it cannot be used on __APPLE__ since we target 10.13 and it was + // only added in 10.15, but we might be compiling on a 10.15 box. + return aligned_alloc(alignment, size); +#elif HAVE_POSIX_MEMALIGN + void* result = NULL; + posix_memalign(&result, alignment, size); + return result; +#else + #error "Platform doesn't support aligned_alloc or posix_memalign" +#endif +} + +void SystemNative_AlignedFree(void* ptr) +{ + free(ptr); +} + +void* SystemNative_AlignedRealloc(void* ptr, uintptr_t alignment, uintptr_t new_size) +{ + void* result = SystemNative_AlignedAlloc(alignment, new_size); + + if (result != NULL) + { + uintptr_t old_size = SystemNative_GetUsableSize(ptr); + assert((ptr != NULL) || (old_size == 0)); + + memcpy(result, ptr, (new_size < old_size) ? new_size : old_size); + SystemNative_AlignedFree(ptr); + } + + return result; } -void* SystemNative_MemReAlloc(void* ptr, uintptr_t size) +void* SystemNative_Calloc(uintptr_t num, uintptr_t size) { - return realloc(ptr, size); + return calloc(num, size); } -void SystemNative_MemFree(void* ptr) +void SystemNative_Free(void* ptr) { free(ptr); } +uintptr_t SystemNative_GetUsableSize(void* ptr) +{ +#if HAVE_MALLOC_SIZE + return malloc_size(ptr); +#elif HAVE_MALLOC_USABLE_SIZE || HAVE_MALLOC_USABLE_SIZE_NP + return malloc_usable_size(ptr); +#else + #error "Platform doesn't support malloc_usable_size or malloc_size" +#endif +} + +void* SystemNative_Malloc(uintptr_t size) +{ + return malloc(size); +} + void* SystemNative_MemSet(void* s, int c, uintptr_t n) { return memset(s, c, n); } + +void* SystemNative_Realloc(void* ptr, uintptr_t new_size) +{ + return realloc(ptr, new_size); +} diff --git a/src/libraries/Native/Unix/System.Native/pal_memory.h b/src/libraries/Native/Unix/System.Native/pal_memory.h index 83fc2f2f9f48a..2cc4c1d8635be 100644 --- a/src/libraries/Native/Unix/System.Native/pal_memory.h +++ b/src/libraries/Native/Unix/System.Native/pal_memory.h @@ -7,21 +7,46 @@ #include "pal_types.h" /** - * C runtime malloc + * C runtime aligned_alloc */ -PALEXPORT void* SystemNative_MemAlloc(uintptr_t size); +PALEXPORT void* SystemNative_AlignedAlloc(uintptr_t alignment, uintptr_t size); /** - * C runtime realloc + * Free for C runtime aligned_alloc + */ +PALEXPORT void SystemNative_AlignedFree(void* ptr); + +/** + * Realloc for C runtime aligned_alloc */ -PALEXPORT void* SystemNative_MemReAlloc(void* ptr, uintptr_t size); +PALEXPORT void* SystemNative_AlignedRealloc(void* ptr, uintptr_t alignment, uintptr_t size); + +/** + * C runtime calloc + */ +PALEXPORT void* SystemNative_Calloc(uintptr_t num, uintptr_t size); /** * C runtime free */ -PALEXPORT void SystemNative_MemFree(void* ptr); +PALEXPORT void SystemNative_Free(void* ptr); + +/** + * Get usable size of C runtime malloc + */ +PALEXPORT uintptr_t SystemNative_GetUsableSize(void* ptr); + +/** + * C runtime malloc + */ +PALEXPORT void* SystemNative_Malloc(uintptr_t size); /** * C runtime memset */ PALEXPORT void* SystemNative_MemSet(void* s, int c, uintptr_t n); + +/** + * C runtime realloc + */ +PALEXPORT void* SystemNative_Realloc(void* ptr, uintptr_t new_size); diff --git a/src/libraries/Native/Unix/configure.cmake b/src/libraries/Native/Unix/configure.cmake index 9d40db9dbf212..b2cfdb3bc40c2 100644 --- a/src/libraries/Native/Unix/configure.cmake +++ b/src/libraries/Native/Unix/configure.cmake @@ -529,9 +529,27 @@ if (CLR_CMAKE_TARGET_LINUX) set(HAVE_SUPPORT_FOR_DUAL_MODE_IPV4_PACKET_INFO 1) endif () +check_symbol_exists( + malloc_size + malloc/malloc.h + HAVE_MALLOC_SIZE) +check_symbol_exists( + malloc_usable_size + malloc.h + HAVE_MALLOC_USABLE_SIZE) +check_symbol_exists( + malloc_usable_size + malloc_np.h + HAVE_MALLOC_USABLE_SIZE_NP) +check_symbol_exists( + posix_memalign + stdlib.h + HAVE_POSIX_MEMALIGN) + if(CLR_CMAKE_TARGET_IOS) # Manually set results from check_c_source_runs() since it's not possible to actually run it during CMake configure checking unset(HAVE_SHM_OPEN_THAT_WORKS_WELL_ENOUGH_WITH_MMAP) + unset(HAVE_ALIGNED_ALLOC) # only exists on iOS 13+ unset(HAVE_CLOCK_MONOTONIC) # only exists on iOS 10+ unset(HAVE_CLOCK_REALTIME) # only exists on iOS 10+ unset(HAVE_FORK) # exists but blocked by kernel @@ -539,23 +557,35 @@ elseif(CLR_CMAKE_TARGET_MACCATALYST) # Manually set results from check_c_source_runs() since it's not possible to actually run it during CMake configure checking # TODO: test to see if these all actually hold true on Mac Catalyst unset(HAVE_SHM_OPEN_THAT_WORKS_WELL_ENOUGH_WITH_MMAP) + unset(HAVE_ALIGNED_ALLOC) # only exists on iOS 13+ unset(HAVE_CLOCK_MONOTONIC) # only exists on iOS 10+ unset(HAVE_CLOCK_REALTIME) # only exists on iOS 10+ unset(HAVE_FORK) # exists but blocked by kernel elseif(CLR_CMAKE_TARGET_TVOS) # Manually set results from check_c_source_runs() since it's not possible to actually run it during CMake configure checking unset(HAVE_SHM_OPEN_THAT_WORKS_WELL_ENOUGH_WITH_MMAP) + unset(HAVE_ALIGNED_ALLOC) # only exists on iOS 13+ unset(HAVE_CLOCK_MONOTONIC) # only exists on iOS 10+ unset(HAVE_CLOCK_REALTIME) # only exists on iOS 10+ unset(HAVE_FORK) # exists but blocked by kernel elseif(CLR_CMAKE_TARGET_ANDROID) # Manually set results from check_c_source_runs() since it's not possible to actually run it during CMake configure checking unset(HAVE_SHM_OPEN_THAT_WORKS_WELL_ENOUGH_WITH_MMAP) + unset(HAVE_ALIGNED_ALLOC) # only exists on newer Android set(HAVE_CLOCK_MONOTONIC 1) set(HAVE_CLOCK_REALTIME 1) -elseif (CLR_CMAKE_TARGET_BROWSER) +elseif(CLR_CMAKE_TARGET_BROWSER) set(HAVE_FORK 0) else() + if(CLR_CMAKE_TARGET_OSX) + unset(HAVE_ALIGNED_ALLOC) # only exists on OSX 10.15+ + else() + check_symbol_exists( + aligned_alloc + stdlib.h + HAVE_ALIGNED_ALLOC) + endif() + check_c_source_runs( " #include diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index 99b032174387f..7c80c23f63241 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -813,6 +813,9 @@ The elements of the AdjustmentRule array must be in chronological order and must not overlap. + + The alignment must be a power of two. + The object already has a CCW associated with it. @@ -3784,4 +3787,4 @@ A MemberInfo that matches '{0}' could not be found. - \ No newline at end of file + diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index a189be9ac1517..a7ce085d9a065 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -824,6 +824,7 @@ + @@ -1725,6 +1726,9 @@ Common\Interop\Windows\Shell32\Interop.SHGetKnownFolderPath.cs + + Common\Interop\Windows\Ucrtbase\Interop.MemAlloc.cs + Common\Interop\Windows\User32\Interop.Constants.cs @@ -1796,6 +1800,7 @@ + @@ -2067,6 +2072,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/BitOperations.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/BitOperations.cs index 49fbb26ff4ca4..b5ffd5e777d98 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/BitOperations.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/BitOperations.cs @@ -72,9 +72,21 @@ public static class BitOperations public static bool IsPow2(ulong value) => (value & (value - 1)) == 0 && value != 0; /// - /// Round the given integral value up to a power of 2. + /// Evaluate whether a given integral value is a power of 2. /// /// The value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool IsPow2(nint value) => (value & (value - 1)) == 0 && value > 0; + + /// + /// Evaluate whether a given integral value is a power of 2. + /// + /// The value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool IsPow2(nuint value) => (value & (value - 1)) == 0 && value != 0; + + /// Round the given integral value up to a power of 2. + /// The value. /// /// The smallest power of 2 which is greater than or equal to . /// If is 0 or the result overflows, returns 0. diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.Unix.cs index 08ab87a390fff..49ef8223c370f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.Unix.cs @@ -62,73 +62,37 @@ internal static unsafe void GetAnsiStringBytes(ReadOnlySpan chars, Span AllocHGlobal((nint)(uint)cb); public static void FreeCoTaskMem(IntPtr ptr) => FreeHGlobal(ptr); - public static IntPtr ReAllocCoTaskMem(IntPtr pv, int cb) + public static unsafe IntPtr ReAllocCoTaskMem(IntPtr pv, int cb) { nuint cbNative = (nuint)(uint)cb; + void* pvNative = (void*)(nint)pv; - if (cbNative == 0) + if ((cbNative == 0) && (pvNative != null)) { - if (pv != IntPtr.Zero) - { - Interop.Sys.MemFree(pv); - return IntPtr.Zero; - } - // Avoid undefined realloc behavior by always allocating at least one byte - cbNative = 1; + Interop.Sys.Free(pvNative); + return IntPtr.Zero; } - IntPtr pNewMem = Interop.Sys.MemReAlloc(pv, cbNative); - if (pNewMem == IntPtr.Zero) - { - throw new OutOfMemoryException(); - } - return pNewMem; + return (nint)NativeMemory.Realloc((void*)(nint)pv, cbNative); } internal static unsafe IntPtr AllocBSTR(int length) @@ -137,22 +101,24 @@ internal static unsafe IntPtr AllocBSTR(int length) const nuint WIN32_ALLOC_ALIGN = 15; ulong cbNative = 2 * (ulong)(uint)length + (uint)sizeof(IntPtr) + (uint)sizeof(char) + WIN32_ALLOC_ALIGN; + if (cbNative > uint.MaxValue) { throw new OutOfMemoryException(); } - IntPtr p = Interop.Sys.MemAlloc((nuint)cbNative & ~WIN32_ALLOC_ALIGN); - if (p == IntPtr.Zero) + void* p = Interop.Sys.Malloc((nuint)cbNative & ~WIN32_ALLOC_ALIGN); + + if (p == null) { throw new OutOfMemoryException(); } - IntPtr s = p + sizeof(IntPtr); + void* s = (byte*)p + sizeof(nuint); *(((uint*)s) - 1) = (uint)(length * sizeof(char)); ((char*)s)[length] = '\0'; - return s; + return (nint)s; } internal static unsafe IntPtr AllocBSTRByteLen(uint length) @@ -161,32 +127,36 @@ internal static unsafe IntPtr AllocBSTRByteLen(uint length) const nuint WIN32_ALLOC_ALIGN = 15; ulong cbNative = (ulong)(uint)length + (uint)sizeof(IntPtr) + (uint)sizeof(char) + WIN32_ALLOC_ALIGN; + if (cbNative > uint.MaxValue) { throw new OutOfMemoryException(); } - IntPtr p = Interop.Sys.MemAlloc((nuint)cbNative & ~WIN32_ALLOC_ALIGN); - if (p == IntPtr.Zero) + void* p = Interop.Sys.Malloc((nuint)cbNative & ~WIN32_ALLOC_ALIGN); + + if (p == null) { throw new OutOfMemoryException(); } - IntPtr s = p + sizeof(IntPtr); + void* s = (byte*)p + sizeof(nuint); *(((uint*)s) - 1) = (uint)length; // NULL-terminate with both a narrow and wide zero. *(byte*)((byte*)s + length) = (byte)'\0'; *(short*)((byte*)s + ((length + 1) & ~1)) = 0; - return s; + return (nint)s; } public static unsafe void FreeBSTR(IntPtr ptr) { - if (ptr != IntPtr.Zero) + void* ptrNative = (void*)(nint)ptr; + + if (ptrNative != null) { - Interop.Sys.MemFree(ptr - sizeof(IntPtr)); + Interop.Sys.Free((byte*)ptr - sizeof(nuint)); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeMemory.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeMemory.Unix.cs new file mode 100644 index 0000000000000..4dc1f1b7f5aa5 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeMemory.Unix.cs @@ -0,0 +1,210 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices +{ + /// This class contains methods that are mainly used to manage native memory. + public static unsafe partial class NativeMemory + { + /// Allocates an aligned block of memory of the specified size and alignment, in bytes. + /// The size, in bytes, of the block to allocate. + /// The alignment, in bytes, of the block to allocate. This must be a power of 2. + /// A pointer to the allocated aligned block of memory. + /// is not a power of two. + /// Allocating of memory with failed. + /// + /// This method allows to be 0 and will return a valid pointer that should not be dereferenced and that should be passed to free to avoid memory leaks. + /// This method is a thin wrapper over the C aligned_alloc API or a platform dependent aligned allocation API such as _aligned_malloc on Win32. + /// This method is not compatible with or , instead or should be called. + /// + [CLSCompliant(false)] + public static void* AlignedAlloc(nuint byteCount, nuint alignment) + { + if (!BitOperations.IsPow2(alignment)) + { + // The C standard doesn't define what a valid alignment is, however Windows and POSIX requires a power of 2 + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_AlignmentMustBePow2); + } + + // The C standard and POSIX requires size to be a multiple of alignment and we want an "empty" allocation for zero + // POSIX additionally requires alignment to be at least sizeof(void*) + + // The adjustment for byteCount can overflow here, and such overflow is generally "harmless". This is because of the + // requirement that alignment be a power of two and that byteCount be a multiple of alignment. Given both of these + // constraints we should only overflow for byteCount > (nuint.MaxValue & ~(alignment - 1)). When such an overflow + // occurs we will get a result that is less than alignment which will cause the allocation to fail. + // + // However, posix_memalign differs from aligned_alloc in that it may return a valid pointer for zero and we need to + // ensure we OOM for this scenario (which can occur for `nuint.MaxValue`) and so we have to check the adjusted size. + + nuint adjustedAlignment = Math.Max(alignment, (uint)sizeof(void*)); + nuint adjustedByteCount = (byteCount != 0) ? (byteCount + (adjustedAlignment - 1)) & ~(adjustedAlignment - 1) : adjustedAlignment; + + void* result = (adjustedByteCount < byteCount) ? null : Interop.Sys.AlignedAlloc(adjustedAlignment, adjustedByteCount); + + if (result == null) + { + ThrowHelper.ThrowOutOfMemoryException(); + } + + return result; + } + + /// Frees an aligned block of memory. + /// A pointer to the aligned block of memory that should be freed. + /// + /// This method does nothing if is null. + /// This method is a thin wrapper over the C free API or a platform dependent aligned free API such as _aligned_free on Win32. + /// + [CLSCompliant(false)] + public static void AlignedFree(void* ptr) + { + if (ptr != null) + { + Interop.Sys.AlignedFree(ptr); + } + } + + /// Reallocates an aligned block of memory of the specified size and alignment, in bytes. + /// The previously allocated block of memory. + /// The size, in bytes, of the block to allocate. + /// The alignment, in bytes, of the block to allocate. This must be a power of 2. + /// A pointer to the reallocated aligned block of memory. + /// is not a power of two. + /// Reallocating of memory with failed. + /// + /// This method acts as if is null. + /// This method allows to be 0 and will return a valid pointer that should not be dereferenced and that should be passed to free to avoid memory leaks. + /// This method is a platform dependent aligned reallocation API such as _aligned_realloc on Win32. + /// This method is not compatible with or , instead or should be called. + /// + [CLSCompliant(false)] + public static void* AlignedRealloc(void* ptr, nuint byteCount, nuint alignment) + { + if (!BitOperations.IsPow2(alignment)) + { + // The C standard doesn't define what a valid alignment is, however Windows and POSIX requires a power of 2 + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_AlignmentMustBePow2); + } + + // The C standard and POSIX requires size to be a multiple of alignment and we want an "empty" allocation for zero + // POSIX additionally requires alignment to be at least sizeof(void*) + + // The adjustment for byteCount can overflow here, and such overflow is generally "harmless". This is because of the + // requirement that alignment be a power of two and that byteCount be a multiple of alignment. Given both of these + // constraints we should only overflow for byteCount > (nuint.MaxValue & ~(alignment - 1)). When such an overflow + // occurs we will get a result that is less than alignment which will cause the allocation to fail. + // + // However, posix_memalign differs from aligned_alloc in that it may return a valid pointer for zero and we need to + // ensure we OOM for this scenario (which can occur for `nuint.MaxValue`) and so we have to check the adjusted size. + + nuint adjustedAlignment = Math.Max(alignment, (uint)sizeof(void*)); + nuint adjustedByteCount = (byteCount != 0) ? (byteCount + (adjustedAlignment - 1)) & ~(adjustedAlignment - 1) : adjustedAlignment; + + void* result = (adjustedByteCount < byteCount) ? null : Interop.Sys.AlignedRealloc(ptr, adjustedAlignment, adjustedByteCount); + + if (result == null) + { + ThrowHelper.ThrowOutOfMemoryException(); + } + + return result; + } + + /// Allocates a block of memory of the specified size, in bytes. + /// The size, in bytes, of the block to allocate. + /// A pointer to the allocated block of memory. + /// Allocating of memory failed. + /// + /// This method allows to be 0 and will return a valid pointer that should not be dereferenced and that should be passed to free to avoid memory leaks. + /// This method is a thin wrapper over the C malloc API. + /// + [CLSCompliant(false)] + public static void* Alloc(nuint byteCount) + { + // The C standard does not define what happens when size == 0, we want an "empty" allocation + void* result = Interop.Sys.Malloc((byteCount != 0) ? byteCount : 1); + + if (result == null) + { + ThrowHelper.ThrowOutOfMemoryException(); + } + + return result; + } + + /// Allocates and zeroes a block of memory of the specified size, in elements. + /// The count, in elements, of the block to allocate. + /// The size, in bytes, of each element in the allocation. + /// A pointer to the allocated and zeroed block of memory. + /// Allocating * bytes of memory failed. + /// + /// This method allows and/or to be 0 and will return a valid pointer that should not be dereferenced and that should be passed to free to avoid memory leaks. + /// This method is a thin wrapper over the C calloc API. + /// + [CLSCompliant(false)] + public static void* AllocZeroed(nuint elementCount, nuint elementSize) + { + void* result = null; + + if ((elementCount != 0) && (elementSize != 0)) + { + result = Interop.Sys.Calloc(elementCount, elementSize); + } + else + { + // The C standard does not define what happens when num == 0 or size == 0, we want an "empty" allocation + result = Interop.Sys.Malloc(1); + } + + if (result == null) + { + ThrowHelper.ThrowOutOfMemoryException(); + } + + return result; + } + + /// Frees a block of memory. + /// A pointer to the block of memory that should be freed. + /// + /// This method does nothing if is null. + /// This method is a thin wrapper over the C free API. + /// + [CLSCompliant(false)] + public static void Free(void* ptr) + { + if (ptr != null) + { + Interop.Sys.Free(ptr); + } + } + + /// Reallocates a block of memory to be the specified size, in bytes. + /// The previously allocated block of memory. + /// The size, in bytes, of the reallocated block. + /// A pointer to the reallocated block of memory. + /// Reallocating of memory failed. + /// + /// This method acts as if is null. + /// This method allows to be 0 and will return a valid pointer that should not be dereferenced and that should be passed to free to avoid memory leaks. + /// This method is a thin wrapper over the C realloc API. + /// + [CLSCompliant(false)] + public static void* Realloc(void* ptr, nuint byteCount) + { + // The C standard does not define what happens when size == 0, we want an "empty" allocation + void* result = Interop.Sys.Realloc(ptr, (byteCount != 0) ? byteCount : 1); + + if (result == null) + { + ThrowHelper.ThrowOutOfMemoryException(); + } + + return result; + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeMemory.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeMemory.Windows.cs new file mode 100644 index 0000000000000..7fbaf933ef924 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeMemory.Windows.cs @@ -0,0 +1,175 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices +{ + /// This class contains methods that are mainly used to manage native memory. + public static unsafe partial class NativeMemory + { + /// Allocates an aligned block of memory of the specified size and alignment, in bytes. + /// The size, in bytes, of the block to allocate. + /// The alignment, in bytes, of the block to allocate. This must be a power of 2. + /// A pointer to the allocated aligned block of memory. + /// is not a power of two. + /// Allocating of memory with failed. + /// + /// This method allows to be 0 and will return a valid pointer that should not be dereferenced and that should be passed to free to avoid memory leaks. + /// This method is a thin wrapper over the C aligned_alloc API or a platform dependent aligned allocation API such as _aligned_malloc on Win32. + /// This method is not compatible with or , instead or should be called. + /// + [CLSCompliant(false)] + public static void* AlignedAlloc(nuint byteCount, nuint alignment) + { + if (!BitOperations.IsPow2(alignment)) + { + // The C standard doesn't define what a valid alignment is, however Windows and POSIX The Windows implementation requires a power of 2 + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_AlignmentMustBePow2); + } + + // Unlike the C standard and POSIX, Windows does not requires size to be a multiple of alignment. However, we do want an "empty" allocation for zero + void* result = Interop.Ucrtbase._aligned_malloc((byteCount != 0) ? byteCount : 1, alignment); + + if (result == null) + { + ThrowHelper.ThrowOutOfMemoryException(); + } + + return result; + } + + /// Frees an aligned block of memory. + /// A pointer to the aligned block of memory that should be freed. + /// + /// This method does nothing if is null. + /// This method is a thin wrapper over the C free API or a platform dependent aligned free API such as _aligned_free on Win32. + /// + [CLSCompliant(false)] + public static void AlignedFree(void* ptr) + { + if (ptr != null) + { + Interop.Ucrtbase._aligned_free(ptr); + } + } + + /// Reallocates an aligned block of memory of the specified size and alignment, in bytes. + /// The previously allocated block of memory. + /// The size, in bytes, of the block to allocate. + /// The alignment, in bytes, of the block to allocate. This must be a power of 2. + /// A pointer to the reallocated aligned block of memory. + /// is not a power of two. + /// Reallocating of memory with failed. + /// + /// This method acts as if is null. + /// This method allows to be 0 and will return a valid pointer that should not be dereferenced and that should be passed to free to avoid memory leaks. + /// This method is a platform dependent aligned reallocation API such as _aligned_realloc on Win32. + /// This method is not compatible with or , instead or should be called. + /// + [CLSCompliant(false)] + public static void* AlignedRealloc(void* ptr, nuint byteCount, nuint alignment) + { + if (!BitOperations.IsPow2(alignment)) + { + // The C standard doesn't define what a valid alignment is, however Windows and POSIX The Windows implementation requires a power of 2 + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_AlignmentMustBePow2); + } + + // Unlike the C standard and POSIX, Windows does not requires size to be a multiple of alignment. However, we do want an "empty" allocation for zero + void* result = Interop.Ucrtbase._aligned_realloc(ptr, (byteCount != 0) ? byteCount : 1, alignment); + + if (result == null) + { + ThrowHelper.ThrowOutOfMemoryException(); + } + + return result; + } + + /// Allocates a block of memory of the specified size, in bytes. + /// The size, in bytes, of the block to allocate. + /// A pointer to the allocated block of memory. + /// Allocating of memory failed. + /// + /// This method allows to be 0 and will return a valid pointer that should not be dereferenced and that should be passed to free to avoid memory leaks. + /// This method is a thin wrapper over the C malloc API. + /// + [CLSCompliant(false)] + public static void* Alloc(nuint byteCount) + { + // The Windows implementation handles size == 0 as we expect + void* result = Interop.Ucrtbase.malloc(byteCount); + + if (result == null) + { + ThrowHelper.ThrowOutOfMemoryException(); + } + + return result; + } + + /// Allocates and zeroes a block of memory of the specified size, in elements. + /// The count, in elements, of the block to allocate. + /// The size, in bytes, of each element in the allocation. + /// A pointer to the allocated and zeroed block of memory. + /// Allocating * bytes of memory failed. + /// + /// This method allows and/or to be 0 and will return a valid pointer that should not be dereferenced and that should be passed to free to avoid memory leaks. + /// This method is a thin wrapper over the C calloc API. + /// + [CLSCompliant(false)] + public static void* AllocZeroed(nuint elementCount, nuint elementSize) + { + // The Windows implementation handles num == 0 && size == 0 as we expect + void* result = Interop.Ucrtbase.calloc(elementCount, elementSize); + + if (result == null) + { + ThrowHelper.ThrowOutOfMemoryException(); + } + + return result; + } + + /// Frees a block of memory. + /// A pointer to the block of memory that should be freed. + /// + /// This method does nothing if is null. + /// This method is a thin wrapper over the C free API. + /// + [CLSCompliant(false)] + public static void Free(void* ptr) + { + if (ptr != null) + { + Interop.Ucrtbase.free(ptr); + } + } + + /// Reallocates a block of memory to be the specified size, in bytes. + /// The previously allocated block of memory. + /// The size, in bytes, of the reallocated block. + /// A pointer to the reallocated block of memory. + /// Reallocating of memory failed. + /// + /// This method acts as if is null. + /// This method allows to be 0 and will return a valid pointer that should not be dereferenced and that should be passed to free to avoid memory leaks. + /// This method is a thin wrapper over the C realloc API. + /// + [CLSCompliant(false)] + public static void* Realloc(void* ptr, nuint byteCount) + { + // The Windows implementation treats size == 0 as Free, we want an "empty" allocation + void* result = Interop.Ucrtbase.realloc(ptr, (byteCount != 0) ? byteCount : 1); + + if (result == null) + { + ThrowHelper.ThrowOutOfMemoryException(); + } + + return result; + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeMemory.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeMemory.cs new file mode 100644 index 0000000000000..b46655ec58c00 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeMemory.cs @@ -0,0 +1,53 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices +{ + public static unsafe partial class NativeMemory + { + /// Allocates a block of memory of the specified size, in elements. + /// The count, in elements, of the block to allocate. + /// The size, in bytes, of each element in the allocation. + /// A pointer to the allocated block of memory. + /// Allocating * bytes of memory failed. + /// + /// This method allows and/or to be 0 and will return a valid pointer that should not be dereferenced and that should be passed to free to avoid memory leaks. + /// This method is a thin wrapper over the C malloc API. + /// + [CLSCompliant(false)] + public static void* Alloc(nuint elementCount, nuint elementSize) + { + nuint byteCount = GetByteCount(elementCount, elementSize); + return Alloc(byteCount); + } + + /// Allocates and zeroes a block of memory of the specified size, in bytes. + /// The size, in bytes, of the block to allocate. + /// A pointer to the allocated and zeroed block of memory. + /// Allocating of memory failed. + /// + /// This method allows to be 0 and will return a valid pointer that should not be dereferenced and that should be passed to free to avoid memory leaks. + /// This method is a thin wrapper over the C calloc API. + /// + [CLSCompliant(false)] + public static void* AllocZeroed(nuint byteCount) + { + return AllocZeroed(byteCount, elementSize: 1); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint GetByteCount(nuint elementCount, nuint elementSize) + { + // This is based on the `mi_count_size_overflow` and `mi_mul_overflow` methods from microsoft/mimalloc. + // Original source is Copyright (c) 2019 Microsoft Corporation, Daan Leijen. Licensed under the MIT license + + // sqrt(nuint.MaxValue) + nuint multiplyNoOverflow = (nuint)1 << (4 * sizeof(nuint)); + + return ((elementSize >= multiplyNoOverflow) || (elementCount >= multiplyNoOverflow)) && (elementSize > 0) && ((nuint.MaxValue / elementSize) < elementCount) ? nuint.MaxValue : (elementCount * elementSize); + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs index 94b2e6d0b18d7..9a5ef787d7c78 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs @@ -975,6 +975,8 @@ private static string GetResourceString(ExceptionResource resource) return SR.Argument_InvalidFlag; case ExceptionResource.CancellationTokenSource_Disposed: return SR.CancellationTokenSource_Disposed; + case ExceptionResource.Argument_AlignmentMustBePow2: + return SR.Argument_AlignmentMustBePow2; default: Debug.Fail("The enum value is not defined, please check the ExceptionResource Enum."); return ""; @@ -1158,5 +1160,6 @@ internal enum ExceptionResource Argument_SpansMustHaveSameLength, Argument_InvalidFlag, CancellationTokenSource_Disposed, + Argument_AlignmentMustBePow2, } } diff --git a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs index 0cf9affe8da76..02e739ad73173 100644 --- a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs +++ b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs @@ -760,6 +760,27 @@ public static void SetDllImportResolver(System.Reflection.Assembly assembly, Sys public static bool TryLoad(string libraryPath, out System.IntPtr handle) { throw null; } public static bool TryLoad(string libraryName, System.Reflection.Assembly assembly, System.Runtime.InteropServices.DllImportSearchPath? searchPath, out System.IntPtr handle) { throw null; } } + public static unsafe partial class NativeMemory + { + [System.CLSCompliantAttribute(false)] + public static void* AlignedAlloc(nuint byteCount, nuint alignment) { throw null; } + [System.CLSCompliantAttribute(false)] + public static void AlignedFree(void* ptr) { } + [System.CLSCompliantAttribute(false)] + public static void* AlignedRealloc(void* ptr, nuint byteCount, nuint alignment) { throw null; } + [System.CLSCompliantAttribute(false)] + public static void* Alloc(nuint byteCount) { throw null; } + [System.CLSCompliantAttribute(false)] + public static void* Alloc(nuint elementCount, nuint elementSize) { throw null; } + [System.CLSCompliantAttribute(false)] + public static void* AllocZeroed(nuint byteCount) { throw null; } + [System.CLSCompliantAttribute(false)] + public static void* AllocZeroed(nuint elementCount, nuint elementSize) { throw null; } + [System.CLSCompliantAttribute(false)] + public static void Free(void* ptr) { } + [System.CLSCompliantAttribute(false)] + public static void* Realloc(void* ptr, nuint byteCount) { throw null; } + } public readonly struct NFloat : IEquatable { public NFloat(float value) { } diff --git a/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj b/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj index e04dab6046036..9ed8c6ff8a776 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj +++ b/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj @@ -14,6 +14,7 @@ + diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/NativeMemoryTests.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/NativeMemoryTests.cs new file mode 100644 index 0000000000000..9787751d87ae2 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/NativeMemoryTests.cs @@ -0,0 +1,439 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; + +namespace System.Runtime.InteropServices.Tests +{ + public unsafe class NativeMemoryTests + { + [Theory] + [InlineData(1)] + [InlineData(2)] + [InlineData(4)] + [InlineData(8)] + [InlineData(16)] + [InlineData(32)] + [InlineData(64)] + [InlineData(128)] + [InlineData(256)] + [InlineData(512)] + [InlineData(1 * 1024)] + [InlineData(2 * 1024)] + [InlineData(4 * 1024)] + [InlineData(8 * 1024)] + [InlineData(16 * 1024)] + [InlineData(64 * 1024)] + [InlineData(1 * 1024 * 1024)] + [InlineData(2 * 1024 * 1024)] + [InlineData(4 * 1024 * 1024)] + public void AlignedAllocTest(uint alignment) + { + void* ptr = NativeMemory.AlignedAlloc(1, alignment); + + Assert.True(ptr != null); + Assert.True((nuint)ptr % alignment == 0); + + NativeMemory.AlignedFree(ptr); + } + + [Fact] + public void AlignedAllocLessThanVoidPtrAlignmentTest() + { + void* ptr = NativeMemory.AlignedAlloc(1, 1); + Assert.True(ptr != null); + NativeMemory.AlignedFree(ptr); + } + + [Fact] + public void AlignedAllocOOMTest() + { + Assert.Throws(() => NativeMemory.AlignedAlloc(nuint.MaxValue - ((uint)sizeof(nuint) - 1), (uint)sizeof(nuint))); + } + + [Fact] + public void AlignedAllocZeroAlignmentTest() + { + Assert.Throws(() => NativeMemory.AlignedAlloc((uint)sizeof(nuint), 0)); + } + + [Fact] + public void AlignedAllocNonPowerOfTwoAlignmentTest() + { + Assert.Throws(() => NativeMemory.AlignedAlloc((uint)sizeof(nuint), (uint)sizeof(nuint) + 1)); + Assert.Throws(() => NativeMemory.AlignedAlloc((uint)sizeof(nuint), (uint)sizeof(nuint) * 3)); + } + + [Fact] + public void AlignedAllocOverflowByteCountTest() + { + // POSIX requires byteCount to be a multiple of alignment and so we will internally upsize. + // This upsizing can overflow for certain values since we do (byteCount + (alignment - 1)) & ~(alignment - 1) + // + // However, this overflow is "harmless" since it will result in a value that is less than alignment + // given that alignment is a power of two and will ultimately be a value less than alignment which + // will be treated as invalid and result in OOM. + // + // Take for example a 64-bit system where the max power of two is (1UL << 63): 9223372036854775808 + // * 9223372036854775808 + 9223372036854775807 == ulong.MaxValue, so no overflow + // * 9223372036854775809 + 9223372036854775807 == 0, so overflows and is less than alignment + // * ulong.MaxValue + 9223372036854775807 == 9223372036854775806, so overflows and is less than alignment + // + // Likewise, for small alignments such as 8 (which is the smallest on a 64-bit system for POSIX): + // * 18446744073709551608 + 7 == ulong.MaxValue, so no overflow + // * 18446744073709551609 + 7 == 0, so overflows and is less than alignment + // * ulong.MaxValue + 7 == 6, so overflows and is less than alignment + + nuint maxAlignment = (nuint)1 << ((sizeof(nuint) * 8) - 1); + Assert.Throws(() => NativeMemory.AlignedAlloc(maxAlignment + 1, maxAlignment)); + + Assert.Throws(() => NativeMemory.AlignedAlloc(nuint.MaxValue, (uint)sizeof(nuint))); + } + + [Fact] + public void AlignedAllocZeroSizeTest() + { + void* ptr = NativeMemory.AlignedAlloc(0, (uint)sizeof(nuint)); + + Assert.True(ptr != null); + Assert.True((nuint)ptr % (uint)sizeof(nuint) == 0); + + NativeMemory.AlignedFree(ptr); + } + + [Fact] + public void AlignedFreeTest() + { + // This should not throw + NativeMemory.AlignedFree(null); + } + + [Theory] + [InlineData(1)] + [InlineData(2)] + [InlineData(4)] + [InlineData(8)] + [InlineData(16)] + [InlineData(32)] + [InlineData(64)] + [InlineData(128)] + [InlineData(256)] + [InlineData(512)] + [InlineData(1 * 1024)] + [InlineData(2 * 1024)] + [InlineData(4 * 1024)] + [InlineData(8 * 1024)] + [InlineData(16 * 1024)] + [InlineData(64 * 1024)] + [InlineData(1 * 1024 * 1024)] + [InlineData(2 * 1024 * 1024)] + [InlineData(4 * 1024 * 1024)] + public void AlignedReallocTest(uint alignment) + { + void* ptr = NativeMemory.AlignedAlloc(1, alignment); + + Assert.True(ptr != null); + Assert.True((nuint)ptr % alignment == 0); + + void* newPtr = NativeMemory.AlignedRealloc(ptr, 1, alignment); + + Assert.True(newPtr != null); + Assert.True((nuint)newPtr % alignment == 0); + + NativeMemory.AlignedFree(newPtr); + } + + [Fact] + public void AlignedReallocLessThanVoidPtrAlignmentTest() + { + void* ptr = NativeMemory.AlignedAlloc(1, 1); + Assert.True(ptr != null); + + void* newPtr = NativeMemory.AlignedRealloc(ptr, 1, 1); + Assert.True(newPtr != null); + NativeMemory.AlignedFree(newPtr); + } + + [Fact] + public void AlignedReallocNullPtrTest() + { + void* ptr = NativeMemory.AlignedRealloc(null, 1, (uint)sizeof(nuint)); + + Assert.True(ptr != null); + Assert.True((nuint)ptr % (uint)sizeof(nuint) == 0); + + NativeMemory.AlignedFree(ptr); + } + + [Fact] + public void AlignedReallocNullPtrOOMTest() + { + Assert.Throws(() => NativeMemory.AlignedRealloc(null, nuint.MaxValue, (uint)sizeof(nuint))); + } + + [Fact] + public void AlignedReallocNullPtrZeroSizeTest() + { + void* ptr = NativeMemory.AlignedRealloc(null, 0, (uint)sizeof(nuint)); + + Assert.True(ptr != null); + Assert.True((nuint)ptr % (uint)sizeof(nuint) == 0); + + NativeMemory.AlignedFree(ptr); + } + + [Fact] + public void AlignedReallocZeroAlignmentTest() + { + void* ptr = NativeMemory.AlignedAlloc(1, (uint)sizeof(nuint)); + + Assert.True(ptr != null); + Assert.True((nuint)ptr % (uint)sizeof(nuint) == 0); + + Assert.Throws(() => NativeMemory.AlignedRealloc(ptr, (uint)sizeof(nuint), 0)); + NativeMemory.AlignedFree(ptr); + } + + [Fact] + public void AlignedReallocNonPowerOfTwoAlignmentTest() + { + void* ptr = NativeMemory.AlignedAlloc(1, (uint)sizeof(nuint)); + + Assert.True(ptr != null); + Assert.True((nuint)ptr % (uint)sizeof(nuint) == 0); + + Assert.Throws(() => NativeMemory.AlignedRealloc(ptr, (uint)sizeof(nuint), (uint)sizeof(nuint) + 1)); + Assert.Throws(() => NativeMemory.AlignedRealloc(ptr, (uint)sizeof(nuint), (uint)sizeof(nuint) * 3)); + NativeMemory.AlignedFree(ptr); + } + + [Fact] + public void AlignedReallocZeroSizeTest() + { + void* ptr = NativeMemory.AlignedAlloc(1, (uint)sizeof(nuint)); + + Assert.True(ptr != null); + Assert.True((nuint)ptr % (uint)sizeof(nuint) == 0); + + void* newPtr = NativeMemory.AlignedRealloc(ptr, 0, (uint)sizeof(nuint)); + + Assert.True(newPtr != null); + Assert.True((nuint)newPtr % (uint)sizeof(nuint) == 0); + + NativeMemory.AlignedFree(newPtr); + } + + [Fact] + public void AlignedReallocSmallerToLargerTest() + { + void* ptr = NativeMemory.AlignedAlloc(16, 16); + + Assert.True(ptr != null); + Assert.True((nuint)ptr % 16 == 0); + + for (int i = 0; i < 16; i++) + { + ((byte*)ptr)[i] = (byte)i; + } + + void* newPtr = NativeMemory.AlignedRealloc(ptr, 32, 16); + + Assert.True(newPtr != null); + Assert.True((nuint)newPtr % 16 == 0); + + for (int i = 0; i < 16; i++) + { + Assert.True(((byte*)newPtr)[i] == i); + } + + NativeMemory.AlignedFree(newPtr); + } + + [Fact] + public void AllocByteCountTest() + { + void* ptr = NativeMemory.Alloc(1); + Assert.True(ptr != null); + NativeMemory.Free(ptr); + } + + [Fact] + public void AllocElementCountTest() + { + void* ptr = NativeMemory.Alloc(1, 1); + Assert.True(ptr != null); + NativeMemory.Free(ptr); + } + + [Fact] + public void AllocByteCountOOMTest() + { + Assert.Throws(() => NativeMemory.Alloc(nuint.MaxValue)); + } + + [Fact] + public void AllocElementCountOOMTest() + { + Assert.Throws(() => NativeMemory.Alloc(1, nuint.MaxValue)); + Assert.Throws(() => NativeMemory.Alloc(nuint.MaxValue, 1)); + Assert.Throws(() => NativeMemory.Alloc(nuint.MaxValue, nuint.MaxValue)); + } + + [Fact] + public void AllocZeroByteCountTest() + { + void* ptr = NativeMemory.Alloc(0); + Assert.True(ptr != null); + NativeMemory.Free(ptr); + } + + [Fact] + public void AllocZeroElementCountTest() + { + void* ptr = NativeMemory.Alloc(0, 1); + Assert.True(ptr != null); + NativeMemory.Free(ptr); + } + + [Fact] + public void AllocZeroElementSizeTest() + { + void* ptr = NativeMemory.Alloc(1, 0); + Assert.True(ptr != null); + NativeMemory.Free(ptr); + } + + [Fact] + public void AllocZeroedByteCountTest() + { + void* ptr = NativeMemory.AllocZeroed(1); + + Assert.True(ptr != null); + Assert.Equal(expected: 0, actual: ((byte*)ptr)[0]); + + NativeMemory.Free(ptr); + } + + [Fact] + public void AllocZeroedElementCountTest() + { + void* ptr = NativeMemory.AllocZeroed(1, 1); + + Assert.True(ptr != null); + Assert.Equal(expected: 0, actual: ((byte*)ptr)[0]); + + NativeMemory.Free(ptr); + } + + [Fact] + public void AllocZeroedByteCountOOMTest() + { + Assert.Throws(() => NativeMemory.AllocZeroed(nuint.MaxValue)); + } + + [Fact] + public void AllocZeroedElementCountOOMTest() + { + Assert.Throws(() => NativeMemory.AllocZeroed(1, nuint.MaxValue)); + Assert.Throws(() => NativeMemory.AllocZeroed(nuint.MaxValue, 1)); + Assert.Throws(() => NativeMemory.AllocZeroed(nuint.MaxValue, nuint.MaxValue)); + } + + [Fact] + public void AllocZeroedZeroByteCountTest() + { + void* ptr = NativeMemory.AllocZeroed(0); + Assert.True(ptr != null); + NativeMemory.Free(ptr); + } + + [Fact] + public void AllocZeroedZeroElementCountTest() + { + void* ptr = NativeMemory.AllocZeroed(0, 1); + Assert.True(ptr != null); + NativeMemory.Free(ptr); + } + + [Fact] + public void AllocZeroedZeroElementSizeTest() + { + void* ptr = NativeMemory.AllocZeroed(1, 0); + Assert.True(ptr != null); + NativeMemory.Free(ptr); + } + + [Fact] + public void FreeTest() + { + // This should not throw + NativeMemory.Free(null); + } + + [Fact] + public void ReallocTest() + { + void* ptr = NativeMemory.Alloc(1); + Assert.True(ptr != null); + + void* newPtr = NativeMemory.Realloc(ptr, 1); + Assert.True(newPtr != null); + NativeMemory.Free(newPtr); + } + + [Fact] + public void ReallocNullPtrTest() + { + void* ptr = NativeMemory.Realloc(null, 1); + Assert.True(ptr != null); + NativeMemory.Free(ptr); + } + + [Fact] + public void ReallocNullPtrOOMTest() + { + Assert.Throws(() => NativeMemory.Realloc(null, nuint.MaxValue)); + } + + [Fact] + public void ReallocNullPtrZeroSizeTest() + { + void* ptr = NativeMemory.Realloc(null, 0); + Assert.True(ptr != null); + NativeMemory.Free(ptr); + } + + [Fact] + public void ReallocZeroSizeTest() + { + void* ptr = NativeMemory.Alloc(1); + Assert.True(ptr != null); + + void* newPtr = NativeMemory.Realloc(ptr, 0); + Assert.True(newPtr != null); + NativeMemory.Free(newPtr); + } + + [Fact] + public void ReallocSmallerToLargerTest() + { + void* ptr = NativeMemory.Alloc(16); + Assert.True(ptr != null); + + for (int i = 0; i < 16; i++) + { + ((byte*)ptr)[i] = (byte)i; + } + + void* newPtr = NativeMemory.Realloc(ptr, 32); + Assert.True(newPtr != null); + + for (int i = 0; i < 16; i++) + { + Assert.True(((byte*)newPtr)[i] == i); + } + + NativeMemory.Free(newPtr); + } + } +} From 472f11c10678d3144284f1eae06be5933e08c3f6 Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Fri, 18 Jun 2021 09:00:05 +0200 Subject: [PATCH 010/107] fix MsQuicStream counting on failure (#54249) --- .../Net/Quic/Implementations/MsQuic/MsQuicStream.cs | 8 ++++---- .../System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs | 6 ++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs index 2813f860318e4..69bdec7abca86 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs @@ -74,7 +74,6 @@ private sealed class State internal MsQuicStream(MsQuicConnection.State connectionState, SafeMsQuicStreamHandle streamHandle, QUIC_STREAM_OPEN_FLAGS flags) { _state.Handle = streamHandle; - _state.ConnectionState = connectionState; _canRead = true; _canWrite = !flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL); _started = true; @@ -99,6 +98,8 @@ internal MsQuicStream(MsQuicConnection.State connectionState, SafeMsQuicStreamHa throw new ObjectDisposedException(nameof(QuicConnection)); } + _state.ConnectionState = connectionState; + if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info( @@ -113,10 +114,8 @@ internal MsQuicStream(MsQuicConnection.State connectionState, QUIC_STREAM_OPEN_F { Debug.Assert(connectionState.Handle != null); - _state.ConnectionState = connectionState; _canRead = !flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL); _canWrite = true; - _stateHandle = GCHandle.Alloc(_state); try { @@ -146,6 +145,8 @@ internal MsQuicStream(MsQuicConnection.State connectionState, QUIC_STREAM_OPEN_F throw new ObjectDisposedException(nameof(QuicConnection)); } + _state.ConnectionState = connectionState; + if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info( @@ -569,7 +570,6 @@ private void Dispose(bool disposing) Marshal.FreeHGlobal(_state.SendQuicBuffers); if (_stateHandle.IsAllocated) _stateHandle.Free(); CleanupSendState(_state); - Debug.Assert(_state.ConnectionState != null); _state.ConnectionState?.RemoveStream(this); if (NetEventSource.Log.IsEnabled()) diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs index 612da52b26172..277265a17a53a 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs @@ -116,7 +116,6 @@ public async Task ConnectWithCertificateChain() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/52048")] public async Task WaitForAvailableUnidirectionStreamsAsyncWorks() { using QuicListener listener = CreateQuicListener(maxUnidirectionalStreams: 1); @@ -126,7 +125,7 @@ public async Task WaitForAvailableUnidirectionStreamsAsyncWorks() using QuicConnection serverConnection = await listener.AcceptConnectionAsync(); await clientTask; - // No stream openned yet, should return immediately. + // No stream opened yet, should return immediately. Assert.True(clientConnection.WaitForAvailableUnidirectionalStreamsAsync().IsCompletedSuccessfully); // Open one stream, should wait till it closes. @@ -141,7 +140,6 @@ public async Task WaitForAvailableUnidirectionStreamsAsyncWorks() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/52048")] public async Task WaitForAvailableBidirectionStreamsAsyncWorks() { using QuicListener listener = CreateQuicListener(maxBidirectionalStreams: 1); @@ -151,7 +149,7 @@ public async Task WaitForAvailableBidirectionStreamsAsyncWorks() using QuicConnection serverConnection = await listener.AcceptConnectionAsync(); await clientTask; - // No stream openned yet, should return immediately. + // No stream opened yet, should return immediately. Assert.True(clientConnection.WaitForAvailableBidirectionalStreamsAsync().IsCompletedSuccessfully); // Open one stream, should wait till it closes. From 140120290aaaffb40e90e8ece982b4f0170c6700 Mon Sep 17 00:00:00 2001 From: Michael Simons Date: Fri, 18 Jun 2021 02:52:22 -0500 Subject: [PATCH 011/107] Exclude experimental projects from source-build (#54222) --- src/coreclr/.nuget/coreclr-packages.proj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/coreclr/.nuget/coreclr-packages.proj b/src/coreclr/.nuget/coreclr-packages.proj index cef8381866cf1..1347e06d5ff0a 100644 --- a/src/coreclr/.nuget/coreclr-packages.proj +++ b/src/coreclr/.nuget/coreclr-packages.proj @@ -5,11 +5,14 @@ + + + + - From 5221db92ed579f8ffa56f1d8e84f07ba6d896077 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Fri, 18 Jun 2021 22:49:46 +1200 Subject: [PATCH 012/107] HTTP/3: Remove ASP.NET Core specific code from QPackEncoder (#54378) --- .../aspnetcore/Http3/QPack/QPackEncoder.cs | 92 +------------------ 1 file changed, 1 insertion(+), 91 deletions(-) diff --git a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/QPack/QPackEncoder.cs b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/QPack/QPackEncoder.cs index 2cc0c4d850a27..1f2275ebb15ff 100644 --- a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/QPack/QPackEncoder.cs +++ b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/QPack/QPackEncoder.cs @@ -9,10 +9,8 @@ namespace System.Net.Http.QPack { - internal sealed class QPackEncoder + internal static class QPackEncoder { - private IEnumerator>? _enumerator; - // https://tools.ietf.org/html/draft-ietf-quic-qpack-11#section-4.5.2 // 0 1 2 3 4 5 6 7 // +---+---+---+---+---+---+---+---+ @@ -403,93 +401,5 @@ private static bool EncodeHeaderBlockPrefix(Span destination, out int byte return true; } - - public bool BeginEncode(IEnumerable> headers, Span buffer, out int length) - { - _enumerator = headers.GetEnumerator(); - - bool hasValue = _enumerator.MoveNext(); - Debug.Assert(hasValue == true); - - buffer[0] = 0; - buffer[1] = 0; - - bool doneEncode = Encode(buffer.Slice(2), out length); - - // Add two for the first two bytes. - length += 2; - return doneEncode; - } - - public bool BeginEncode(int statusCode, IEnumerable> headers, Span buffer, out int length) - { - _enumerator = headers.GetEnumerator(); - - bool hasValue = _enumerator.MoveNext(); - Debug.Assert(hasValue == true); - - // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#header-prefix - buffer[0] = 0; - buffer[1] = 0; - - int statusCodeLength = EncodeStatusCode(statusCode, buffer.Slice(2)); - bool done = Encode(buffer.Slice(statusCodeLength + 2), throwIfNoneEncoded: false, out int headersLength); - length = statusCodeLength + headersLength + 2; - - return done; - } - - public bool Encode(Span buffer, out int length) - { - return Encode(buffer, throwIfNoneEncoded: true, out length); - } - - private bool Encode(Span buffer, bool throwIfNoneEncoded, out int length) - { - length = 0; - - do - { - if (!EncodeLiteralHeaderFieldWithoutNameReference(_enumerator!.Current.Key, _enumerator.Current.Value, buffer.Slice(length), out int headerLength)) - { - if (length == 0 && throwIfNoneEncoded) - { - throw new QPackEncodingException("TODO sync with corefx" /* CoreStrings.HPackErrorNotEnoughBuffer */); - } - return false; - } - - length += headerLength; - } while (_enumerator.MoveNext()); - - return true; - } - - private int EncodeStatusCode(int statusCode, Span buffer) - { - switch (statusCode) - { - case 200: - case 204: - case 206: - case 304: - case 400: - case 404: - case 500: - EncodeStaticIndexedHeaderField(H3StaticTable.StatusIndex[statusCode], buffer, out var bytesWritten); - return bytesWritten; - default: - // https://tools.ietf.org/html/draft-ietf-quic-qpack-21#section-4.5.4 - // Index is 63 - :status - buffer[0] = 0b01011111; - buffer[1] = 0b00110000; - - ReadOnlySpan statusBytes = StatusCodes.ToStatusBytes(statusCode); - buffer[2] = (byte)statusBytes.Length; - statusBytes.CopyTo(buffer.Slice(3)); - - return 3 + statusBytes.Length; - } - } } } \ No newline at end of file From f37f0b9819664e0680f20975d4da33fb86b63ad2 Mon Sep 17 00:00:00 2001 From: Gleb Balykov Date: Fri, 18 Jun 2021 15:38:22 +0300 Subject: [PATCH 013/107] Fix Linux x86 build (#50836) --- .../superpmi-shim-collector.cpp | 4 ++-- .../superpmi-shim-counter.cpp | 4 ++-- .../superpmi-shim-simple.cpp | 4 ++-- src/coreclr/clrdefinitions.cmake | 8 +++++--- src/coreclr/debug/ee/funceval.cpp | 2 +- src/coreclr/debug/ee/i386/x86walker.cpp | 2 +- src/coreclr/gc/unix/gcenv.unix.cpp | 2 +- src/coreclr/gcinfo/CMakeLists.txt | 4 ++++ src/coreclr/jit/CMakeLists.txt | 4 ++++ src/coreclr/md/inc/VerifyLayouts.inc | 3 +++ src/coreclr/md/inc/metamodel.h | 1 + src/coreclr/pal/src/misc/sysinfo.cpp | 2 +- src/coreclr/vm/callingconvention.h | 9 +++++---- src/coreclr/vm/codeman.cpp | 4 ++-- src/coreclr/vm/common.h | 1 + src/coreclr/vm/crossgencompile.cpp | 5 +++++ src/coreclr/vm/exceptionhandling.cpp | 4 ++-- src/coreclr/vm/fcall.h | 3 +++ src/coreclr/vm/field.cpp | 19 +++++++++++++++++++ src/coreclr/vm/field.h | 17 +---------------- src/coreclr/vm/jithelpers.cpp | 12 ++++++++---- src/coreclr/vm/jitinterface.cpp | 2 +- 22 files changed, 74 insertions(+), 42 deletions(-) diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/superpmi-shim-collector.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/superpmi-shim-collector.cpp index f1f61fcc67209..9cd2200710753 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/superpmi-shim-collector.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/superpmi-shim-collector.cpp @@ -130,7 +130,7 @@ extern "C" return TRUE; } -extern "C" DLLEXPORT void __stdcall jitStartup(ICorJitHost* host) +extern "C" DLLEXPORT void jitStartup(ICorJitHost* host) { // crossgen2 doesn't invoke DllMain on Linux/Mac (under PAL), so optionally do initialization work here. InitializeShim(); @@ -157,7 +157,7 @@ extern "C" DLLEXPORT void __stdcall jitStartup(ICorJitHost* host) pnjitStartup(g_ourJitHost); } -extern "C" DLLEXPORT ICorJitCompiler* __stdcall getJit() +extern "C" DLLEXPORT ICorJitCompiler* getJit() { DWORD dwRetVal = 0; PgetJit pngetJit; diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/superpmi-shim-counter.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/superpmi-shim-counter.cpp index af0ceac75d269..b2ad7f6754a53 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/superpmi-shim-counter.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/superpmi-shim-counter.cpp @@ -108,7 +108,7 @@ extern "C" return TRUE; } -extern "C" DLLEXPORT void __stdcall jitStartup(ICorJitHost* host) +extern "C" DLLEXPORT void jitStartup(ICorJitHost* host) { SetDefaultPaths(); SetLibName(); @@ -141,7 +141,7 @@ extern "C" DLLEXPORT void __stdcall jitStartup(ICorJitHost* host) pnjitStartup(g_ourJitHost); } -extern "C" DLLEXPORT ICorJitCompiler* __stdcall getJit() +extern "C" DLLEXPORT ICorJitCompiler* getJit() { DWORD dwRetVal = 0; PgetJit pngetJit; diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/superpmi-shim-simple.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/superpmi-shim-simple.cpp index ac720a16983b0..c6c24afb08933 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/superpmi-shim-simple.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/superpmi-shim-simple.cpp @@ -93,7 +93,7 @@ extern "C" return TRUE; } -extern "C" DLLEXPORT void __stdcall jitStartup(ICorJitHost* host) +extern "C" DLLEXPORT void jitStartup(ICorJitHost* host) { SetDefaultPaths(); SetLibName(); @@ -117,7 +117,7 @@ extern "C" DLLEXPORT void __stdcall jitStartup(ICorJitHost* host) pnjitStartup(g_ourJitHost); } -extern "C" DLLEXPORT ICorJitCompiler* __stdcall getJit() +extern "C" DLLEXPORT ICorJitCompiler* getJit() { DWORD dwRetVal = 0; PgetJit pngetJit; diff --git a/src/coreclr/clrdefinitions.cmake b/src/coreclr/clrdefinitions.cmake index 425d3d84d6a13..eeb421cac4c2f 100644 --- a/src/coreclr/clrdefinitions.cmake +++ b/src/coreclr/clrdefinitions.cmake @@ -57,12 +57,14 @@ if(CLR_CMAKE_HOST_WIN32) add_definitions(-D_CRT_SECURE_NO_WARNINGS) endif(CLR_CMAKE_HOST_WIN32) -add_compile_definitions($<$>>:EnC_SUPPORTED>) -if(CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_I386) +if (NOT (CLR_CMAKE_TARGET_ARCH_I386 AND CLR_CMAKE_TARGET_UNIX)) + add_compile_definitions($<$>>:EnC_SUPPORTED>) +endif() +if(CLR_CMAKE_TARGET_ARCH_AMD64 OR (CLR_CMAKE_TARGET_ARCH_I386 AND CLR_CMAKE_TARGET_WIN32)) if(CLR_CMAKE_TARGET_WIN32) add_compile_definitions($<$>>:FEATURE_ENC_SUPPORTED>) endif(CLR_CMAKE_TARGET_WIN32) -endif(CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_I386) +endif(CLR_CMAKE_TARGET_ARCH_AMD64 OR (CLR_CMAKE_TARGET_ARCH_I386 AND CLR_CMAKE_TARGET_WIN32)) # Features - please keep them alphabetically sorted if(CLR_CMAKE_TARGET_WIN32) diff --git a/src/coreclr/debug/ee/funceval.cpp b/src/coreclr/debug/ee/funceval.cpp index 41acc0cb92b9d..de23343b7979b 100644 --- a/src/coreclr/debug/ee/funceval.cpp +++ b/src/coreclr/debug/ee/funceval.cpp @@ -1635,7 +1635,7 @@ static void GCProtectAllPassedArgs(DebuggerEval *pDE, #endif } #endif // TARGET_X86 - + FALLTHROUGH; default: // // Ignorable - no need to protect diff --git a/src/coreclr/debug/ee/i386/x86walker.cpp b/src/coreclr/debug/ee/i386/x86walker.cpp index ef7dabb3be4c2..cb262e770ecdc 100644 --- a/src/coreclr/debug/ee/i386/x86walker.cpp +++ b/src/coreclr/debug/ee/i386/x86walker.cpp @@ -415,7 +415,7 @@ void NativeWalker::DecodeInstructionForPatchSkip(const BYTE *address, Instructio case 2: case 3: pInstrAttrib->m_fIsCall = true; - // fall through + FALLTHROUGH; case 4: case 5: pInstrAttrib->m_fIsAbsBranch = true; diff --git a/src/coreclr/gc/unix/gcenv.unix.cpp b/src/coreclr/gc/unix/gcenv.unix.cpp index 9408a9b121406..d2798c860adee 100644 --- a/src/coreclr/gc/unix/gcenv.unix.cpp +++ b/src/coreclr/gc/unix/gcenv.unix.cpp @@ -884,7 +884,7 @@ static size_t GetLogicalProcessorCacheSizeFromOS() cacheSize = std::max(cacheSize, ( size_t) sysconf(_SC_LEVEL4_CACHE_SIZE)); #endif -#if defined(TARGET_LINUX) && !defined(HOST_ARM) +#if defined(TARGET_LINUX) && !defined(HOST_ARM) && !defined(HOST_X86) if (cacheSize == 0) { // diff --git a/src/coreclr/gcinfo/CMakeLists.txt b/src/coreclr/gcinfo/CMakeLists.txt index 70b0f7396d89b..bfddaee4b700a 100644 --- a/src/coreclr/gcinfo/CMakeLists.txt +++ b/src/coreclr/gcinfo/CMakeLists.txt @@ -82,3 +82,7 @@ create_gcinfo_lib(TARGET gcinfo_unix_armel OS unix ARCH armel) create_gcinfo_lib(TARGET gcinfo_unix_arm OS unix ARCH arm) create_gcinfo_lib(TARGET gcinfo_win_arm OS win ARCH arm) create_gcinfo_lib(TARGET gcinfo_win_x86 OS win ARCH x86) + +if (CLR_CMAKE_TARGET_ARCH_I386 AND CLR_CMAKE_TARGET_UNIX) + create_gcinfo_lib(TARGET gcinfo_unix_x86 OS unix ARCH x86) +endif (CLR_CMAKE_TARGET_ARCH_I386 AND CLR_CMAKE_TARGET_UNIX) diff --git a/src/coreclr/jit/CMakeLists.txt b/src/coreclr/jit/CMakeLists.txt index 892203e81aa94..f06c011e5b963 100644 --- a/src/coreclr/jit/CMakeLists.txt +++ b/src/coreclr/jit/CMakeLists.txt @@ -516,6 +516,10 @@ target_compile_definitions(clrjit_unix_arm_${ARCH_HOST_NAME} PRIVATE ARM_SOFTFP create_standalone_jit(TARGET clrjit_win_arm_${ARCH_HOST_NAME} OS win ARCH arm DESTINATIONS .) create_standalone_jit(TARGET clrjit_win_x86_${ARCH_HOST_NAME} OS win ARCH x86 DESTINATIONS .) +if (CLR_CMAKE_TARGET_ARCH_I386 AND CLR_CMAKE_TARGET_UNIX) + create_standalone_jit(TARGET clrjit_unix_x86_${ARCH_HOST_NAME} OS unix ARCH x86 DESTINATIONS .) +endif (CLR_CMAKE_TARGET_ARCH_I386 AND CLR_CMAKE_TARGET_UNIX) + if (CLR_CMAKE_TARGET_UNIX) if (NOT ARCH_TARGET_NAME STREQUAL s390x) install_clr(TARGETS clrjit_unix_${ARCH_TARGET_NAME}_${ARCH_HOST_NAME} DESTINATIONS . COMPONENT jit) diff --git a/src/coreclr/md/inc/VerifyLayouts.inc b/src/coreclr/md/inc/VerifyLayouts.inc index 14e068ecefa3e..2ca6384b86841 100644 --- a/src/coreclr/md/inc/VerifyLayouts.inc +++ b/src/coreclr/md/inc/VerifyLayouts.inc @@ -156,6 +156,9 @@ FIELD(CLiteWeightStgdbRW, m_wszFileName, sizeof(void*)) FIELD(CLiteWeightStgdbRW, m_dwDatabaseLFT, 4) FIELD(CLiteWeightStgdbRW, m_dwDatabaseLFS, 4) FIELD(CLiteWeightStgdbRW, m_pStgIO, sizeof(void*)) +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB +FIELD(CLiteWeightStgdbRW, m_pPdbHeap, sizeof(void*)) +#endif END_TYPE(CLiteWeightStgdbRW, 8) USING_ALIAS(CLiteWeightStgdb__CMiniMdRW__, CLiteWeightStgdb) diff --git a/src/coreclr/md/inc/metamodel.h b/src/coreclr/md/inc/metamodel.h index e3206a589ad99..a1cb17724601e 100644 --- a/src/coreclr/md/inc/metamodel.h +++ b/src/coreclr/md/inc/metamodel.h @@ -594,6 +594,7 @@ class CMiniMdBase : public IMetaModelCommonRO protected: + DAC_ALIGNAS(8) CMiniMdSchema m_Schema; // data header. ULONG m_TblCount; // Tables in this database. BOOL m_fVerifiedByTrustedSource; // whether the data was verified by a trusted source diff --git a/src/coreclr/pal/src/misc/sysinfo.cpp b/src/coreclr/pal/src/misc/sysinfo.cpp index 1a9ca8fbfba72..8f935b3e3ea1a 100644 --- a/src/coreclr/pal/src/misc/sysinfo.cpp +++ b/src/coreclr/pal/src/misc/sysinfo.cpp @@ -558,7 +558,7 @@ PAL_GetLogicalProcessorCacheSizeFromOS() cacheSize = std::max(cacheSize, (size_t)sysconf(_SC_LEVEL4_CACHE_SIZE)); #endif -#if defined(TARGET_LINUX) && !defined(HOST_ARM) +#if defined(TARGET_LINUX) && !defined(HOST_ARM) && !defined(HOST_X86) if (cacheSize == 0) { // diff --git a/src/coreclr/vm/callingconvention.h b/src/coreclr/vm/callingconvention.h index 8064adfdfc97f..00cc47e9661d0 100644 --- a/src/coreclr/vm/callingconvention.h +++ b/src/coreclr/vm/callingconvention.h @@ -423,11 +423,12 @@ class ArgIteratorTemplate : public ARGITERATOR_BASE //@todo: Is it more apropos to call LookupApproxFieldTypeHandle() here? TypeHandle fldHnd = pFD->GetApproxFieldTypeHandleThrowing(); CONSISTENCY_CHECK(!fldHnd.IsNull()); - pMT = fldHnd.GetMethodTable(); + pMT = fldHnd.GetMethodTable(); + FALLTHROUGH; } - case ELEMENT_TYPE_PTR: - case ELEMENT_TYPE_I: - case ELEMENT_TYPE_U: + case ELEMENT_TYPE_PTR: + case ELEMENT_TYPE_I: + case ELEMENT_TYPE_U: case ELEMENT_TYPE_I4: case ELEMENT_TYPE_U4: { diff --git a/src/coreclr/vm/codeman.cpp b/src/coreclr/vm/codeman.cpp index 171251a9a71c9..349a76039cd46 100644 --- a/src/coreclr/vm/codeman.cpp +++ b/src/coreclr/vm/codeman.cpp @@ -1718,8 +1718,8 @@ static void LoadAndInitializeJIT(LPCWSTR pwzJitName, OUT HINSTANCE* phJit, OUT I } #ifdef FEATURE_MERGE_JIT_AND_ENGINE -EXTERN_C void __stdcall jitStartup(ICorJitHost* host); -EXTERN_C ICorJitCompiler* __stdcall getJit(); +EXTERN_C void jitStartup(ICorJitHost* host); +EXTERN_C ICorJitCompiler* getJit(); #endif // FEATURE_MERGE_JIT_AND_ENGINE BOOL EEJitManager::LoadJIT() diff --git a/src/coreclr/vm/common.h b/src/coreclr/vm/common.h index 1951003113ec9..f0bade34506c6 100644 --- a/src/coreclr/vm/common.h +++ b/src/coreclr/vm/common.h @@ -367,6 +367,7 @@ inline void ClrRestoreNonvolatileContext(PCONTEXT ContextRecord) #include "pefile.inl" #include "excep.h" #include "method.hpp" +#include "field.h" #include "callingconvention.h" #include "frames.h" #include "qcall.h" diff --git a/src/coreclr/vm/crossgencompile.cpp b/src/coreclr/vm/crossgencompile.cpp index df3555e31a7a9..8622505081b14 100644 --- a/src/coreclr/vm/crossgencompile.cpp +++ b/src/coreclr/vm/crossgencompile.cpp @@ -271,6 +271,11 @@ void CrawlFrame::GetExactGenericInstantiations(Instantiation *pClassInst, Instan UNREACHABLE(); } +void SetObjectReferenceUnchecked(OBJECTREF *dst,OBJECTREF ref) +{ + UNREACHABLE(); +} + BOOL Object::SupportsInterface(OBJECTREF pObj, MethodTable* pInterfaceMT) { UNREACHABLE(); diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index 963df734959f8..7fff234ca85ef 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -1219,7 +1219,7 @@ lExit: ; invalidRevPInvoke = gcInfoDecoder.GetReversePInvokeFrameStackSlot() != NO_REVERSE_PINVOKE_FRAME; #else // USE_GC_INFO_DECODER hdrInfo gcHdrInfo; - DecodeGCHdrInfo(gcInfoToken, 0, &gcHdrInfo); + DecodeGCHdrInfo(codeInfo.GetGCInfoToken(), 0, &gcHdrInfo); invalidRevPInvoke = gcHdrInfo.revPInvokeOffset != INVALID_REV_PINVOKE_OFFSET; #endif // USE_GC_INFO_DECODER @@ -4640,7 +4640,7 @@ VOID DECLSPEC_NORETURN UnwindManagedExceptionPass1(PAL_SEHException& ex, CONTEXT invalidRevPInvoke = gcInfoDecoder.GetReversePInvokeFrameStackSlot() != NO_REVERSE_PINVOKE_FRAME; #else // USE_GC_INFO_DECODER hdrInfo gcHdrInfo; - DecodeGCHdrInfo(gcInfoToken, 0, &gcHdrInfo); + DecodeGCHdrInfo(codeInfo.GetGCInfoToken(), 0, &gcHdrInfo); invalidRevPInvoke = gcHdrInfo.revPInvokeOffset != INVALID_REV_PINVOKE_OFFSET; #endif // USE_GC_INFO_DECODER diff --git a/src/coreclr/vm/fcall.h b/src/coreclr/vm/fcall.h index b330797631de0..ff8e488515fae 100644 --- a/src/coreclr/vm/fcall.h +++ b/src/coreclr/vm/fcall.h @@ -1145,6 +1145,7 @@ struct FCSigCheck { #define HCIMPL2_IV(rettype, funcname, a1, a2) rettype F_CALL_CONV funcname(int /* EAX */, int /* EDX */, a1, a2) { HCIMPL_PROLOG(funcname) #define HCIMPL2VA(rettype, funcname, a1, a2) rettype F_CALL_VA_CONV funcname(a1, a2, ...) { HCIMPL_PROLOG(funcname) #define HCIMPL3(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(int /* EAX */, a2, a1, a3) { HCIMPL_PROLOG(funcname) +#define HCIMPL3_RAW(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(int /* EAX */, a2, a1, a3) { #define HCIMPL4(rettype, funcname, a1, a2, a3, a4) rettype F_CALL_CONV funcname(int /* EAX */, a2, a1, a4, a3) { HCIMPL_PROLOG(funcname) #define HCIMPL5(rettype, funcname, a1, a2, a3, a4, a5) rettype F_CALL_CONV funcname(int /* EAX */, a2, a1, a5, a4, a3) { HCIMPL_PROLOG(funcname) @@ -1169,6 +1170,7 @@ struct FCSigCheck { #define HCIMPL2_IV(rettype, funcname, a1, a2) rettype F_CALL_CONV funcname(a1, a2) { HCIMPL_PROLOG(funcname) #define HCIMPL2VA(rettype, funcname, a1, a2) rettype F_CALL_VA_CONV funcname(a1, a2, ...) { HCIMPL_PROLOG(funcname) #define HCIMPL3(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(a1, a2, a3) { HCIMPL_PROLOG(funcname) +#define HCIMPL3_RAW(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(a1, a2, a3) { #define HCIMPL4(rettype, funcname, a1, a2, a3, a4) rettype F_CALL_CONV funcname(a1, a2, a4, a3) { HCIMPL_PROLOG(funcname) #define HCIMPL5(rettype, funcname, a1, a2, a3, a4, a5) rettype F_CALL_CONV funcname(a1, a2, a5, a4, a3) { HCIMPL_PROLOG(funcname) @@ -1194,6 +1196,7 @@ struct FCSigCheck { #define HCIMPL2_IV(rettype, funcname, a1, a2) rettype F_CALL_CONV funcname(a1, a2) { HCIMPL_PROLOG(funcname) #define HCIMPL2VA(rettype, funcname, a1, a2) rettype F_CALL_VA_CONV funcname(a1, a2, ...) { HCIMPL_PROLOG(funcname) #define HCIMPL3(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(a1, a2, a3) { HCIMPL_PROLOG(funcname) +#define HCIMPL3_RAW(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(a1, a2, a3) { #define HCIMPL4(rettype, funcname, a1, a2, a3, a4) rettype F_CALL_CONV funcname(a1, a2, a3, a4) { HCIMPL_PROLOG(funcname) #define HCIMPL5(rettype, funcname, a1, a2, a3, a4, a5) rettype F_CALL_CONV funcname(a1, a2, a3, a4, a5) { HCIMPL_PROLOG(funcname) diff --git a/src/coreclr/vm/field.cpp b/src/coreclr/vm/field.cpp index 025d9ab8d0b3b..74ff841509817 100644 --- a/src/coreclr/vm/field.cpp +++ b/src/coreclr/vm/field.cpp @@ -18,6 +18,25 @@ #include "peimagelayout.inl" +#ifndef DACCESS_COMPILE +VOID FieldDesc::SetStaticOBJECTREF(OBJECTREF objRef) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + INJECT_FAULT(COMPlusThrowOM()); + } + CONTRACTL_END + + GCPROTECT_BEGIN(objRef); + OBJECTREF *pObjRef = (OBJECTREF *)GetCurrentStaticAddress(); + SetObjectReference(pObjRef, objRef); + GCPROTECT_END(); +} +#endif + // called from code:MethodTableBuilder::InitializeFieldDescs#InitCall VOID FieldDesc::Init(mdFieldDef mb, CorElementType FieldType, DWORD dwMemberAttrs, BOOL fIsStatic, BOOL fIsRVA, BOOL fIsThreadLocal, LPCSTR pszFieldName) { diff --git a/src/coreclr/vm/field.h b/src/coreclr/vm/field.h index be57bec9d0c60..8fa87edc30f26 100644 --- a/src/coreclr/vm/field.h +++ b/src/coreclr/vm/field.h @@ -483,22 +483,7 @@ class FieldDesc return *(OBJECTREF *)GetCurrentStaticAddress(); } - VOID SetStaticOBJECTREF(OBJECTREF objRef) - { - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - INJECT_FAULT(COMPlusThrowOM()); - } - CONTRACTL_END - - GCPROTECT_BEGIN(objRef); - OBJECTREF *pObjRef = (OBJECTREF *)GetCurrentStaticAddress(); - SetObjectReference(pObjRef, objRef); - GCPROTECT_END(); - } + VOID SetStaticOBJECTREF(OBJECTREF objRef); void* GetStaticValuePtr() { diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index b7f92254c7083..ea70bf8e0ac24 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -5471,7 +5471,7 @@ NOINLINE static void JIT_ReversePInvokeEnterRare2(ReversePInvokeFrame* frame, vo // As a result, we specially decorate this method to have the correct calling convention // and argument ordering for an HCALL, but we don't use the HCALL macros and contracts // since this method doesn't follow the contracts. -void F_CALL_CONV HCCALL3(JIT_ReversePInvokeEnterTrackTransitions, ReversePInvokeFrame* frame, CORINFO_METHOD_HANDLE handle, void* secretArg) +HCIMPL3_RAW(void, JIT_ReversePInvokeEnterTrackTransitions, ReversePInvokeFrame* frame, CORINFO_METHOD_HANDLE handle, void* secretArg) { _ASSERTE(frame != NULL && handle != NULL); @@ -5520,8 +5520,9 @@ void F_CALL_CONV HCCALL3(JIT_ReversePInvokeEnterTrackTransitions, ReversePInvoke INSTALL_EXCEPTION_HANDLING_RECORD(&frame->record.m_ExReg); #endif } +HCIMPLEND_RAW -void F_CALL_CONV HCCALL1(JIT_ReversePInvokeEnter, ReversePInvokeFrame* frame) +HCIMPL1_RAW(void, JIT_ReversePInvokeEnter, ReversePInvokeFrame* frame) { _ASSERTE(frame != NULL); @@ -5552,8 +5553,9 @@ void F_CALL_CONV HCCALL1(JIT_ReversePInvokeEnter, ReversePInvokeFrame* frame) INSTALL_EXCEPTION_HANDLING_RECORD(&frame->record.m_ExReg); #endif } +HCIMPLEND_RAW -void F_CALL_CONV HCCALL1(JIT_ReversePInvokeExitTrackTransitions, ReversePInvokeFrame* frame) +HCIMPL1_RAW(void, JIT_ReversePInvokeExitTrackTransitions, ReversePInvokeFrame* frame) { _ASSERTE(frame != NULL); _ASSERTE(frame->currentThread == GetThread()); @@ -5574,8 +5576,9 @@ void F_CALL_CONV HCCALL1(JIT_ReversePInvokeExitTrackTransitions, ReversePInvokeF } #endif } +HCIMPLEND_RAW -void F_CALL_CONV HCCALL1(JIT_ReversePInvokeExit, ReversePInvokeFrame* frame) +HCIMPL1_RAW(void, JIT_ReversePInvokeExit, ReversePInvokeFrame* frame) { _ASSERTE(frame != NULL); _ASSERTE(frame->currentThread == GetThread()); @@ -5589,6 +5592,7 @@ void F_CALL_CONV HCCALL1(JIT_ReversePInvokeExit, ReversePInvokeFrame* frame) UNINSTALL_EXCEPTION_HANDLING_RECORD(&frame->record.m_ExReg); #endif } +HCIMPLEND_RAW //======================================================================== // diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index cb5fe80e0c4de..f4b155a500a0a 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -12526,7 +12526,7 @@ void CEEJitInfo::getEHinfo( #endif // CROSSGEN_COMPILE #if defined(CROSSGEN_COMPILE) -EXTERN_C ICorJitCompiler* __stdcall getJit(); +EXTERN_C ICorJitCompiler* getJit(); #endif // defined(CROSSGEN_COMPILE) #ifdef FEATURE_INTERPRETER From 8d0c26365b715366e5af228318ab915d3d70b6eb Mon Sep 17 00:00:00 2001 From: Michael Simons Date: Fri, 18 Jun 2021 08:26:13 -0500 Subject: [PATCH 014/107] Condition MibcArgs for source-build (#54225) --- src/coreclr/crossgen-corelib.proj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/coreclr/crossgen-corelib.proj b/src/coreclr/crossgen-corelib.proj index 8e9cef4aa638c..6d675a2a59e24 100644 --- a/src/coreclr/crossgen-corelib.proj +++ b/src/coreclr/crossgen-corelib.proj @@ -96,7 +96,8 @@ $(CrossGenDllCmd) -o:$(CoreLibOutputPath) $(CrossGenDllCmd) -r:$([MSBuild]::NormalizePath('$(BinDir)', 'IL', '*.dll')) $(CrossGenDllCmd) --targetarch:$(TargetArchitecture) - $(CrossGenDllCmd) -m:$(MergedMibcPath) --embed-pgo-data + @(OptimizationMibcFiles->'-m:$(MergedMibcPath)', ' ') + $(CrossGenDllCmd) $(MibcArgs) --embed-pgo-data $(CrossGenDllCmd) -O $(CrossGenDllCmd) --verify-type-and-field-layout $(CrossGenDllCmd) $(CoreLibInputPath) From cdf65116d8a4b3fa1b37fa26641419dc6aec3e43 Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Fri, 18 Jun 2021 16:03:39 +0200 Subject: [PATCH 015/107] fix some failing quic tests (#54326) * fix some failing quic tests * feedback from review * add comment --- .../Implementations/MsQuic/MsQuicStream.cs | 13 +++- ...icStreamConnectedStreamConformanceTests.cs | 64 ++++++++----------- 2 files changed, 36 insertions(+), 41 deletions(-) diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs index 69bdec7abca86..e796496bcf1f5 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs @@ -513,8 +513,17 @@ internal override void Shutdown() internal override int Read(Span buffer) { ThrowIfDisposed(); - - return ReadAsync(buffer.ToArray()).AsTask().GetAwaiter().GetResult(); + byte[] rentedBuffer = ArrayPool.Shared.Rent(buffer.Length); + try + { + int readLength = ReadAsync(new Memory(rentedBuffer, 0, buffer.Length)).AsTask().GetAwaiter().GetResult(); + rentedBuffer.AsSpan(0, readLength).CopyTo(buffer); + return readLength; + } + finally + { + ArrayPool.Shared.Return(rentedBuffer); + } } internal override void Write(ReadOnlySpan buffer) diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs index 6c3670bbc30bb..dbf3fae0f65c9 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs @@ -6,6 +6,7 @@ using System.IO.Tests; using System.Net.Quic.Implementations; using System.Net.Security; +using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; using Xunit; @@ -37,59 +38,42 @@ public sealed class MsQuicQuicStreamConformanceTests : QuicStreamConformanceTest // TODO: new additions, find out the actual reason for hanging [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] - public override Task ReadTimeout_Expires_Throws() => base.ReadTimeout_Expires_Throws(); - [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] - public override Task ConcurrentBidirectionalReadsWrites_Success() => base.ConcurrentBidirectionalReadsWrites_Success(); - [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] - public override Task ArgumentValidation_ThrowsExpectedException() => base.ArgumentValidation_ThrowsExpectedException(); - [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] public override Task ReadWriteAsync_PrecanceledOperations_ThrowsCancellationException() => base.ReadWriteAsync_PrecanceledOperations_ThrowsCancellationException(); [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] public override Task Read_DataStoredAtDesiredOffset(ReadWriteMode mode) => base.Read_DataStoredAtDesiredOffset(mode); [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] public override Task ReadAsync_CancelPendingRead_DoesntImpactSubsequentReads() => base.ReadAsync_CancelPendingRead_DoesntImpactSubsequentReads(); [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] - public override Task Disposed_ThrowsObjectDisposedException() => base.Disposed_ThrowsObjectDisposedException(); - [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] - public override Task Timeout_Roundtrips() => base.Timeout_Roundtrips(); - [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] - public override Task ZeroByteWrite_OtherDataReceivedSuccessfully(ReadWriteMode mode) => base.ZeroByteWrite_OtherDataReceivedSuccessfully(mode); - [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] - public override Task ReadAsync_ContinuesOnCurrentTaskSchedulerIfDesired(bool flowExecutionContext, bool? continueOnCapturedContext) => base.ReadAsync_ContinuesOnCurrentTaskSchedulerIfDesired(flowExecutionContext, continueOnCapturedContext); - [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] public override Task ZeroByteRead_BlocksUntilDataAvailableOrNops(ReadWriteMode mode) => base.ZeroByteRead_BlocksUntilDataAvailableOrNops(mode); [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] - public override Task ReadAsync_CancelPendingTask_ThrowsCancellationException() => base.ReadAsync_CancelPendingTask_ThrowsCancellationException(); - [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] - public override Task ReadAsync_ContinuesOnCurrentSynchronizationContextIfDesired(bool flowExecutionContext, bool? continueOnCapturedContext) => base.ReadAsync_ContinuesOnCurrentSynchronizationContextIfDesired(flowExecutionContext, continueOnCapturedContext); - [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] - public override Task ReadWriteByte_Success() => base.ReadWriteByte_Success(); - [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] - public override Task ReadWrite_Success(ReadWriteMode mode, int writeSize, bool startWithFlush) => base.ReadWrite_Success(mode, writeSize, startWithFlush); - [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] - public override Task ReadWrite_Success_Large(ReadWriteMode mode, int writeSize, bool startWithFlush) => base.ReadWrite_Success_Large(mode, writeSize, startWithFlush); - [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] - public override Task Flush_ValidOnWriteableStreamWithNoData_Success() => base.Flush_ValidOnWriteableStreamWithNoData_Success(); - [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] - public override Task ReadAsync_CancelPendingValueTask_ThrowsCancellationException() => base.ReadAsync_CancelPendingValueTask_ThrowsCancellationException(); - [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] public override Task ReadAsync_DuringReadAsync_ThrowsIfUnsupported() => base.ReadAsync_DuringReadAsync_ThrowsIfUnsupported(); - [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] - public override Task ReadWrite_CustomMemoryManager_Success(bool useAsync) => base.ReadWrite_CustomMemoryManager_Success(useAsync); - [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] - public override Task Flush_ValidOnReadableStream_Success() => base.Flush_ValidOnReadableStream_Success(); - } public abstract class QuicStreamConformanceTests : ConnectedStreamConformanceTests { + public X509Certificate2 ServerCertificate = System.Net.Test.Common.Configuration.Certificates.GetServerCertificate(); + + public bool RemoteCertificateValidationCallback(object sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors) + { + Assert.Equal(ServerCertificate.GetCertHash(), certificate?.GetCertHash()); + return true; + } + public SslServerAuthenticationOptions GetSslServerAuthenticationOptions() { return new SslServerAuthenticationOptions() { ApplicationProtocols = new List() { new SslApplicationProtocol("quictest") }, - // TODO: use a cert. MsQuic currently only allows certs that are trusted. - ServerCertificate = System.Net.Test.Common.Configuration.Certificates.GetServerCertificate() + ServerCertificate = ServerCertificate + }; + } + + public SslClientAuthenticationOptions GetSslClientAuthenticationOptions() + { + return new SslClientAuthenticationOptions() + { + ApplicationProtocols = new List() { new SslApplicationProtocol("quictest") }, + RemoteCertificateValidationCallback = RemoteCertificateValidationCallback }; } @@ -98,30 +82,32 @@ public SslServerAuthenticationOptions GetSslServerAuthenticationOptions() protected override async Task CreateConnectedStreamsAsync() { QuicImplementationProvider provider = Provider; - var protocol = new SslApplicationProtocol("quictest"); - var listener = new QuicListener( provider, new IPEndPoint(IPAddress.Loopback, 0), GetSslServerAuthenticationOptions()); + byte[] buffer = new byte[1] { 42 }; QuicConnection connection1 = null, connection2 = null; QuicStream stream1 = null, stream2 = null; - await WhenAllOrAnyFailed( Task.Run(async () => { connection1 = await listener.AcceptConnectionAsync(); stream1 = await connection1.AcceptStreamAsync(); + Assert.Equal(1, await stream1.ReadAsync(buffer)); }), Task.Run(async () => { connection2 = new QuicConnection( provider, listener.ListenEndPoint, - new SslClientAuthenticationOptions() { ApplicationProtocols = new List() { protocol } }); + GetSslClientAuthenticationOptions()); await connection2.ConnectAsync(); stream2 = connection2.OpenBidirectionalStream(); + // OpenBidirectionalStream only allocates ID. We will force stream opening + // by Writing there and receiving data on the other side. + await stream2.WriteAsync(buffer); })); var result = new StreamPairWithOtherDisposables(stream1, stream2); From cff5854fb02259b88cab89f77e8d403809300ae8 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Fri, 18 Jun 2021 07:55:08 -0700 Subject: [PATCH 016/107] Make some p/invokes blittable (#54370) --- .../Interop.Crypto.cs | 14 ++++++++------ .../Kernel32/Interop.GlobalMemoryStatusEx.cs | 2 +- .../Interop/Windows/NtDll/Interop.NtCreateFile.cs | 14 ++++++++------ .../Windows/NtDll/Interop.NtQueryDirectoryFile.cs | 2 +- .../User32/Interop.GetWindowThreadProcessId.cs | 2 +- .../Windows/User32/Interop.IsWindowVisible.cs | 2 +- .../src/System/Diagnostics/ProcessManager.Win32.cs | 4 ++-- .../System/IO/MemoryMappedFiles/Interop.Windows.cs | 2 +- .../IO/Enumeration/FileSystemEnumerator.Win32.cs | 3 ++- .../src/System/Runtime/MemoryFailPoint.Windows.cs | 2 +- .../src/System.Runtime.Caching.csproj | 3 ++- .../Runtime/Caching/MemoryMonitor.Windows.cs | 2 +- .../Caching/PhysicalMemoryMonitor.Windows.cs | 2 +- 13 files changed, 30 insertions(+), 24 deletions(-) diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Crypto.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Crypto.cs index 9e692c79ee1cf..c1d0d991a0e52 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Crypto.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Crypto.cs @@ -68,23 +68,25 @@ internal static int BioTell(SafeBioHandle bio) [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool PushX509StackField(SafeSharedX509StackHandle stack, SafeX509Handle x509); - internal static string? GetX509RootStorePath(out bool defaultPath) + internal static unsafe string? GetX509RootStorePath(out bool defaultPath) { - IntPtr ptr = GetX509RootStorePath_private(out byte usedDefault); + byte usedDefault; + IntPtr ptr = GetX509RootStorePath_private(&usedDefault); defaultPath = (usedDefault != 0); return Marshal.PtrToStringAnsi(ptr); } - internal static string? GetX509RootStoreFile() + internal static unsafe string? GetX509RootStoreFile() { - return Marshal.PtrToStringAnsi(GetX509RootStoreFile_private(out _)); + byte unused; + return Marshal.PtrToStringAnsi(GetX509RootStoreFile_private(&unused)); } [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_GetX509RootStorePath")] - private static extern IntPtr GetX509RootStorePath_private(out byte defaultPath); + private static unsafe extern IntPtr GetX509RootStorePath_private(byte* defaultPath); [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_GetX509RootStoreFile")] - private static extern IntPtr GetX509RootStoreFile_private(out byte defaultPath); + private static unsafe extern IntPtr GetX509RootStoreFile_private(byte* defaultPath); [DllImport(Libraries.CryptoNative)] private static extern int CryptoNative_X509StoreSetVerifyTime( diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GlobalMemoryStatusEx.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GlobalMemoryStatusEx.cs index a5029f5ccb6e8..662cab8383cc7 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GlobalMemoryStatusEx.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GlobalMemoryStatusEx.cs @@ -8,6 +8,6 @@ internal static partial class Interop internal static partial class Kernel32 { [DllImport(Libraries.Kernel32)] - internal static extern bool GlobalMemoryStatusEx(ref MEMORYSTATUSEX lpBuffer); + internal static unsafe extern BOOL GlobalMemoryStatusEx(MEMORYSTATUSEX* lpBuffer); } } diff --git a/src/libraries/Common/src/Interop/Windows/NtDll/Interop.NtCreateFile.cs b/src/libraries/Common/src/Interop/Windows/NtDll/Interop.NtCreateFile.cs index 1a89d911f83bd..7d6ab13b6633d 100644 --- a/src/libraries/Common/src/Interop/Windows/NtDll/Interop.NtCreateFile.cs +++ b/src/libraries/Common/src/Interop/Windows/NtDll/Interop.NtCreateFile.cs @@ -14,10 +14,10 @@ internal static partial class NtDll // https://msdn.microsoft.com/en-us/library/windows/hardware/ff566424.aspx [DllImport(Libraries.NtDll, CharSet = CharSet.Unicode, ExactSpelling = true)] private static extern unsafe uint NtCreateFile( - out IntPtr FileHandle, + IntPtr* FileHandle, DesiredAccess DesiredAccess, - ref OBJECT_ATTRIBUTES ObjectAttributes, - out IO_STATUS_BLOCK IoStatusBlock, + OBJECT_ATTRIBUTES* ObjectAttributes, + IO_STATUS_BLOCK* IoStatusBlock, long* AllocationSize, FileAttributes FileAttributes, FileShare ShareAccess, @@ -55,11 +55,13 @@ internal static unsafe (uint status, IntPtr handle) CreateFile( rootDirectory, securityQualityOfService); + IntPtr handle; + IO_STATUS_BLOCK statusBlock; uint status = NtCreateFile( - out IntPtr handle, + &handle, desiredAccess, - ref attributes, - out IO_STATUS_BLOCK statusBlock, + &attributes, + &statusBlock, AllocationSize: preallocationSize, fileAttributes, shareAccess, diff --git a/src/libraries/Common/src/Interop/Windows/NtDll/Interop.NtQueryDirectoryFile.cs b/src/libraries/Common/src/Interop/Windows/NtDll/Interop.NtQueryDirectoryFile.cs index 517fd4195f360..63b6bf9767c24 100644 --- a/src/libraries/Common/src/Interop/Windows/NtDll/Interop.NtQueryDirectoryFile.cs +++ b/src/libraries/Common/src/Interop/Windows/NtDll/Interop.NtQueryDirectoryFile.cs @@ -16,7 +16,7 @@ public static extern unsafe int NtQueryDirectoryFile( IntPtr Event, IntPtr ApcRoutine, IntPtr ApcContext, - out IO_STATUS_BLOCK IoStatusBlock, + IO_STATUS_BLOCK* IoStatusBlock, IntPtr FileInformation, uint Length, FILE_INFORMATION_CLASS FileInformationClass, diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.GetWindowThreadProcessId.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.GetWindowThreadProcessId.cs index 33863ed425912..3b145fea4cc29 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.GetWindowThreadProcessId.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.GetWindowThreadProcessId.cs @@ -9,7 +9,7 @@ internal static partial class Interop internal static partial class User32 { [DllImport(Libraries.User32, ExactSpelling = true)] - public static extern int GetWindowThreadProcessId(IntPtr handle, out int processId); + public static unsafe extern int GetWindowThreadProcessId(IntPtr handle, int* processId); [DllImport(Libraries.User32, ExactSpelling = true)] public static extern int GetWindowThreadProcessId(HandleRef handle, out int processId); diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.IsWindowVisible.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.IsWindowVisible.cs index 5e819d26db630..15a829a9cf557 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.IsWindowVisible.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.IsWindowVisible.cs @@ -9,6 +9,6 @@ internal static partial class Interop internal static partial class User32 { [DllImport(Libraries.User32)] - public static extern bool IsWindowVisible(IntPtr hWnd); + public static extern BOOL IsWindowVisible(IntPtr hWnd); } } diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Win32.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Win32.cs index 53c9c743de856..e272d5fa4af1f 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Win32.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Win32.cs @@ -42,7 +42,7 @@ public static unsafe IntPtr FindMainWindow(int processId) private static bool IsMainWindow(IntPtr handle) { - return (Interop.User32.GetWindow(handle, GW_OWNER) == IntPtr.Zero) && Interop.User32.IsWindowVisible(handle); + return (Interop.User32.GetWindow(handle, GW_OWNER) == IntPtr.Zero) && Interop.User32.IsWindowVisible(handle) != Interop.BOOL.FALSE; } [UnmanagedCallersOnly] @@ -51,7 +51,7 @@ private static unsafe Interop.BOOL EnumWindowsCallback(IntPtr handle, IntPtr ext MainWindowFinder* instance = (MainWindowFinder*)extraParameter; int processId = 0; // Avoid uninitialized variable if the window got closed in the meantime - Interop.User32.GetWindowThreadProcessId(handle, out processId); + Interop.User32.GetWindowThreadProcessId(handle, &processId); if ((processId == instance->_processId) && IsMainWindow(handle)) { diff --git a/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/Interop.Windows.cs b/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/Interop.Windows.cs index 202c3ead180f2..d30e42c558a63 100644 --- a/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/Interop.Windows.cs +++ b/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/Interop.Windows.cs @@ -13,7 +13,7 @@ public static unsafe void CheckForAvailableVirtualMemory(ulong nativeSize) { Interop.Kernel32.MEMORYSTATUSEX memoryStatus = default; memoryStatus.dwLength = (uint)sizeof(Interop.Kernel32.MEMORYSTATUSEX); - if (Interop.Kernel32.GlobalMemoryStatusEx(ref memoryStatus)) + if (Interop.Kernel32.GlobalMemoryStatusEx(&memoryStatus) != Interop.BOOL.FALSE) { ulong totalVirtual = memoryStatus.ullTotalVirtual; if (nativeSize >= totalVirtual) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerator.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerator.Win32.cs index 22e990194f5d8..3b90d71120eda 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerator.Win32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerator.Win32.cs @@ -20,12 +20,13 @@ private unsafe bool GetData() { Debug.Assert(_directoryHandle != (IntPtr)(-1) && _directoryHandle != IntPtr.Zero && !_lastEntryFound); + Interop.NtDll.IO_STATUS_BLOCK statusBlock; int status = Interop.NtDll.NtQueryDirectoryFile( FileHandle: _directoryHandle, Event: IntPtr.Zero, ApcRoutine: IntPtr.Zero, ApcContext: IntPtr.Zero, - IoStatusBlock: out Interop.NtDll.IO_STATUS_BLOCK statusBlock, + IoStatusBlock: &statusBlock, FileInformation: _buffer, Length: (uint)_bufferLength, FileInformationClass: Interop.NtDll.FILE_INFORMATION_CLASS.FileFullDirectoryInformation, diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/MemoryFailPoint.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/MemoryFailPoint.Windows.cs index 7c4a2da7babb1..d5d1952e7a150 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/MemoryFailPoint.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/MemoryFailPoint.Windows.cs @@ -17,7 +17,7 @@ private static unsafe bool CheckForAvailableMemory(out ulong availPageFile, out { Interop.Kernel32.MEMORYSTATUSEX memoryStatus = default; memoryStatus.dwLength = (uint)sizeof(Interop.Kernel32.MEMORYSTATUSEX); - if (!Interop.Kernel32.GlobalMemoryStatusEx(ref memoryStatus)) + if (Interop.Kernel32.GlobalMemoryStatusEx(&memoryStatus) == Interop.BOOL.FALSE) { availPageFile = default; totalAddressSpaceFree = default; diff --git a/src/libraries/System.Runtime.Caching/src/System.Runtime.Caching.csproj b/src/libraries/System.Runtime.Caching/src/System.Runtime.Caching.csproj index e2a0ebdd80382..0c059eae7c103 100644 --- a/src/libraries/System.Runtime.Caching/src/System.Runtime.Caching.csproj +++ b/src/libraries/System.Runtime.Caching/src/System.Runtime.Caching.csproj @@ -51,6 +51,7 @@ + @@ -85,4 +86,4 @@ - \ No newline at end of file + diff --git a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/MemoryMonitor.Windows.cs b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/MemoryMonitor.Windows.cs index 189a9460d32c5..acb8bb0f42290 100644 --- a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/MemoryMonitor.Windows.cs +++ b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/MemoryMonitor.Windows.cs @@ -16,7 +16,7 @@ static unsafe MemoryMonitor() { Interop.Kernel32.MEMORYSTATUSEX memoryStatus = default; memoryStatus.dwLength = (uint)sizeof(Interop.Kernel32.MEMORYSTATUSEX); - if (Interop.Kernel32.GlobalMemoryStatusEx(ref memoryStatus)) + if (Interop.Kernel32.GlobalMemoryStatusEx(&memoryStatus) != Interop.BOOL.FALSE) { s_totalPhysical = (long)memoryStatus.ullTotalPhys; s_totalVirtual = (long)memoryStatus.ullTotalVirtual; diff --git a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/PhysicalMemoryMonitor.Windows.cs b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/PhysicalMemoryMonitor.Windows.cs index 078bf9ed3794e..6aff99b66b19d 100644 --- a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/PhysicalMemoryMonitor.Windows.cs +++ b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/PhysicalMemoryMonitor.Windows.cs @@ -14,7 +14,7 @@ protected override unsafe int GetCurrentPressure() { Interop.Kernel32.MEMORYSTATUSEX memoryStatus = default; memoryStatus.dwLength = (uint)sizeof(Interop.Kernel32.MEMORYSTATUSEX); - if (!Interop.Kernel32.GlobalMemoryStatusEx(ref memoryStatus)) + if (Interop.Kernel32.GlobalMemoryStatusEx(&memoryStatus) == Interop.BOOL.FALSE) { return 0; } From fd7e92418a3b66bca0926e88f2bf66de13c25ca3 Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Fri, 18 Jun 2021 12:23:40 -0700 Subject: [PATCH 017/107] Create a summary Markdown file for all asm diffs (#54430) Create a per-MCH file summary.md file, then accumulate them all into a single overall summary Markdown file, for use in GitHub comments. Uses the existing `jit-analyze --md` functionality. Also, change asm diffs to not report missing data or asm diffs as a replay failure. Finally, don't overwrite superpmi.log files (or the new diff_summary.md files): create new, unique file names for each run. --- src/coreclr/scripts/superpmi.py | 95 ++++++++++++++++++++++++++------- 1 file changed, 75 insertions(+), 20 deletions(-) diff --git a/src/coreclr/scripts/superpmi.py b/src/coreclr/scripts/superpmi.py index d4033201ca04e..ce4332c314547 100755 --- a/src/coreclr/scripts/superpmi.py +++ b/src/coreclr/scripts/superpmi.py @@ -630,6 +630,39 @@ def create_unique_directory_name(root_directory, base_name): return full_path +def create_unique_file_name(directory, base_name, extension): + """ Create a unique file name in the specified directory by joining `base_name` and `extension`. + If this name already exists, append ".1", ".2", ".3", etc., to the `base_name` + name component until the full file name is not found. + + Args: + directory (str) : directory in which a new file will be created + base_name (str) : the base name of the new filename to be added + extension (str) : the filename extension of the new filename to be added + + Returns: + (str) The full absolute path of the new filename. + """ + + directory = os.path.abspath(directory) + if not os.path.isdir(directory): + try: + os.makedirs(directory) + except Exception as exception: + logging.critical(exception) + raise exception + + full_path = os.path.join(directory, base_name + "." + extension) + + count = 1 + while os.path.isfile(full_path): + new_full_path = os.path.join(directory, base_name + "." + str(count) + "." + extension) + count += 1 + full_path = new_full_path + + return full_path + + def get_files_from_path(path, match_func=lambda path: True): """ Return all files in a directory tree matching a criteria. @@ -1517,14 +1550,6 @@ def replay(self): result = True # Assume success - # Possible return codes from SuperPMI - # - # 0 : success - # -1 : general fatal error (e.g., failed to initialize, failed to read files) - # -2 : JIT failed to initialize - # 1 : there were compilation failures - # 2 : there were assembly diffs - with TempDir() as temp_location: logging.debug("") logging.debug("Temp Location: %s", temp_location) @@ -1596,8 +1621,12 @@ def replay(self): if return_code == 0: logging.info("Clean SuperPMI replay") else: - files_with_replay_failures.append(mch_file) result = False + # Don't report as replay failure missing data (return code 3). + # Anything else, such as compilation failure (return code 1, typically a JIT assert) will be + # reported as a replay failure. + if return_code != 3: + files_with_replay_failures.append(mch_file) if is_nonzero_length_file(fail_mcl_file): # Unclean replay. Examine the contents of the fail.mcl file to dig into failures. @@ -1669,14 +1698,6 @@ def replay_with_asm_diffs(self): result = True # Assume success - # Possible return codes from SuperPMI - # - # 0 : success - # -1 : general fatal error (e.g., failed to initialize, failed to read files) - # -2 : JIT failed to initialize - # 1 : there were compilation failures - # 2 : there were assembly diffs - # Set up some settings we'll use below. asm_complus_vars = { @@ -1744,6 +1765,9 @@ def replay_with_asm_diffs(self): files_with_asm_diffs = [] files_with_replay_failures = [] + # List of all Markdown summary files + all_md_summary_files = [] + with TempDir(self.coreclr_args.temp_dir, self.coreclr_args.skip_cleanup) as temp_location: logging.debug("") logging.debug("Temp Location: %s", temp_location) @@ -1804,8 +1828,12 @@ def replay_with_asm_diffs(self): if return_code == 0: logging.info("Clean SuperPMI replay") else: - files_with_replay_failures.append(mch_file) result = False + # Don't report as replay failure asm diffs (return code 2) or missing data (return code 3). + # Anything else, such as compilation failure (return code 1, typically a JIT assert) will be + # reported as a replay failure. + if return_code != 2 and return_code != 3: + files_with_replay_failures.append(mch_file) artifacts_base_name = create_artifacts_base_name(self.coreclr_args, mch_file) @@ -1938,7 +1966,10 @@ async def create_one_artifact(jit_path: str, location: str, flags) -> str: jit_analyze_path = find_file(jit_analyze_file, path_var.split(os.pathsep)) if jit_analyze_path is not None: # It appears we have a built jit-analyze on the path, so try to run it. - command = [ jit_analyze_path, "-r", "--base", base_asm_location, "--diff", diff_asm_location ] + md_summary_file = os.path.join(asm_root_dir, "summary.md") + summary_file_info = ( mch_file, md_summary_file ) + all_md_summary_files.append(summary_file_info) + command = [ jit_analyze_path, "--md", md_summary_file, "-r", "--base", base_asm_location, "--diff", diff_asm_location ] run_and_log(command, logging.INFO) ran_jit_analyze = True @@ -1971,8 +2002,32 @@ async def create_one_artifact(jit_path: str, location: str, flags) -> str: ################################################################################################ end of for mch_file in self.mch_files + # Report the overall results summary of the asmdiffs run + logging.info("Asm diffs summary:") + # Construct an overall Markdown summary file. + + if len(all_md_summary_files) > 0: + overall_md_summary_file = create_unique_file_name(self.coreclr_args.spmi_location, "diff_summary", "md") + if not os.path.isdir(self.coreclr_args.spmi_location): + os.makedirs(self.coreclr_args.spmi_location) + if os.path.isfile(overall_md_summary_file): + os.remove(overall_md_summary_file) + + with open(overall_md_summary_file, "w") as write_fh: + for summary_file_info in all_md_summary_files: + summary_mch = summary_file_info[0] + summary_mch_filename = os.path.basename(summary_mch) # Display just the MCH filename, not the full path + summary_file = summary_file_info[1] + with open(summary_file, "r") as read_fh: + write_fh.write("## " + summary_mch_filename + ":\n\n") + shutil.copyfileobj(read_fh, write_fh) + + logging.info(" Summary Markdown file: %s", overall_md_summary_file) + + # Report the set of MCH files with asm diffs and replay failures. + if len(files_with_replay_failures) != 0: logging.info(" Replay failures in %s MCH files:", len(files_with_replay_failures)) for file in files_with_replay_failures: @@ -3173,7 +3228,7 @@ def setup_spmi_location_arg(spmi_location): log_file = None if coreclr_args.log_file is None: if hasattr(coreclr_args, "spmi_location"): - log_file = os.path.join(coreclr_args.spmi_location, "superpmi.log") + log_file = create_unique_file_name(coreclr_args.spmi_location, "superpmi", "log") if not os.path.isdir(coreclr_args.spmi_location): os.makedirs(coreclr_args.spmi_location) else: From f722ad0061a628a7b7b3c0772153de71ddfe8196 Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Fri, 18 Jun 2021 21:40:18 +0200 Subject: [PATCH 018/107] set UsableAfterCanceledReads for QUIC tests (#54416) --- .../QuicStreamConnectedStreamConformanceTests.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs index dbf3fae0f65c9..35b54bc6d70b8 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs @@ -21,6 +21,7 @@ public sealed class MockQuicStreamConformanceTests : QuicStreamConformanceTests public sealed class MsQuicQuicStreamConformanceTests : QuicStreamConformanceTests { protected override QuicImplementationProvider Provider => QuicImplementationProviders.MsQuic; + protected override bool UsableAfterCanceledReads => false; // TODO: These are all hanging, likely due to Stream close behavior. [ActiveIssue("https://github.com/dotnet/runtime/issues/756")] @@ -42,8 +43,6 @@ public sealed class MsQuicQuicStreamConformanceTests : QuicStreamConformanceTest [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] public override Task Read_DataStoredAtDesiredOffset(ReadWriteMode mode) => base.Read_DataStoredAtDesiredOffset(mode); [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] - public override Task ReadAsync_CancelPendingRead_DoesntImpactSubsequentReads() => base.ReadAsync_CancelPendingRead_DoesntImpactSubsequentReads(); - [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] public override Task ZeroByteRead_BlocksUntilDataAvailableOrNops(ReadWriteMode mode) => base.ZeroByteRead_BlocksUntilDataAvailableOrNops(mode); [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] public override Task ReadAsync_DuringReadAsync_ThrowsIfUnsupported() => base.ReadAsync_DuringReadAsync_ThrowsIfUnsupported(); From 6737dce33d35970fde2ca349cc1492da977b9699 Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Fri, 18 Jun 2021 23:17:18 +0300 Subject: [PATCH 019/107] Backport bugfixes, infrastructural changes and perf improvements from the polymorphism feature branch (#54420) * backport opportunistic bugfixes * strengthen debug assertions and add clarifying comments * Rework WriteStack/ReadStack implementations * Use array instead of list for storing frames. Previous frames can now be passed by reference * Ensure that the _count field always reflects the current depth. * Remove StackFrame.Reset() methods which are occassionally a source of dirty frames being reused. * Take advantage of JIT optimizations in the serialization hot path * address PR feedback --- .../Object/ObjectDefaultConverter.cs | 4 +- .../Json/Serialization/JsonConverterOfT.cs | 144 ++++++++++-------- .../JsonSerializer.Write.String.cs | 3 - .../Metadata/JsonPropertyInfoOfT.cs | 12 +- .../Text/Json/Serialization/ReadStack.cs | 133 ++++++++-------- .../Text/Json/Serialization/ReadStackFrame.cs | 15 -- .../Text/Json/Serialization/WriteStack.cs | 137 ++++++++--------- .../Json/Serialization/WriteStackFrame.cs | 15 -- .../ReferenceHandlerTests.IgnoreCycles.cs | 21 ++- 9 files changed, 235 insertions(+), 249 deletions(-) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectDefaultConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectDefaultConverter.cs index 4a2be44c8629c..89b9cdad1eeff 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectDefaultConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectDefaultConverter.cs @@ -312,7 +312,9 @@ internal sealed override bool OnTryWrite( if (!jsonPropertyInfo.GetMemberAndWriteJson(objectValue!, ref state, writer)) { - Debug.Assert(jsonPropertyInfo.ConverterBase.ConverterStrategy != ConverterStrategy.Value); + Debug.Assert(jsonPropertyInfo.ConverterBase.ConverterStrategy != ConverterStrategy.Value || + jsonPropertyInfo.ConverterBase.TypeToConvert == JsonTypeInfo.ObjectType); + return false; } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs index 6ea73dd49c9ec..543fbe2f5229a 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs @@ -22,7 +22,7 @@ protected internal JsonConverter() // In the future, this will be check for !IsSealed (and excluding value types). CanBePolymorphic = TypeToConvert == JsonTypeInfo.ObjectType; IsValueType = TypeToConvert.IsValueType; - CanBeNull = !IsValueType || TypeToConvert.IsNullableOfT(); + CanBeNull = default(T) is null; IsInternalConverter = GetType().Assembly == typeof(JsonConverter).Assembly; if (HandleNull) @@ -220,7 +220,12 @@ internal bool TryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSeriali // Remember if we were a continuation here since Push() may affect IsContinuation. bool wasContinuation = state.IsContinuation; +#if DEBUG + // DEBUG: ensure push/pop operations preserve stack integrity + JsonTypeInfo originalJsonTypeInfo = state.Current.JsonTypeInfo; +#endif state.Push(); + Debug.Assert(TypeToConvert.IsAssignableFrom(state.Current.JsonTypeInfo.Type)); #if !DEBUG // For performance, only perform validation on internal converters on debug builds. @@ -257,6 +262,9 @@ internal bool TryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSeriali value = default; state.Pop(true); +#if DEBUG + Debug.Assert(ReferenceEquals(originalJsonTypeInfo, state.Current.JsonTypeInfo)); +#endif return true; } @@ -288,6 +296,9 @@ internal bool TryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSeriali } state.Pop(success); +#if DEBUG + Debug.Assert(ReferenceEquals(originalJsonTypeInfo, state.Current.JsonTypeInfo)); +#endif return success; } @@ -298,6 +309,9 @@ internal override sealed bool TryReadAsObject(ref Utf8JsonReader reader, JsonSer return success; } + /// + /// Overridden by the nullable converter to prevent boxing of values by the JIT. + /// internal virtual bool IsNull(in T value) => value == null; internal bool TryWrite(Utf8JsonWriter writer, in T value, JsonSerializerOptions options, ref WriteStack state) @@ -307,7 +321,7 @@ internal bool TryWrite(Utf8JsonWriter writer, in T value, JsonSerializerOptions ThrowHelper.ThrowJsonException_SerializerCycleDetected(options.EffectiveMaxDepth); } - if (CanBeNull && !HandleNullOnWrite && IsNull(value)) + if (default(T) is null && !HandleNullOnWrite && IsNull(value)) { // We do not pass null values to converters unless HandleNullOnWrite is true. Null values for properties were // already handled in GetMemberAndWriteJson() so we don't need to check for IgnoreNullValues here. @@ -317,81 +331,76 @@ internal bool TryWrite(Utf8JsonWriter writer, in T value, JsonSerializerOptions bool ignoreCyclesPopReference = false; - if (options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.IgnoreCycles && - // .NET types that are serialized as JSON primitive values don't need to be tracked for cycle detection e.g: string. - ConverterStrategy != ConverterStrategy.Value && - !IsValueType && !IsNull(value)) + if ( +#if NET6_0_OR_GREATER + !typeof(T).IsValueType && // treated as a constant by recent versions of the JIT. +#else + !IsValueType && +#endif + value is not null) { - // Custom (user) converters shall not track references - // it is responsibility of the user to break cycles in case there's any - // if we compare against Preserve, objects don't get preserved when a custom converter exists - // given that the custom converter executes prior to the preserve logic. - Debug.Assert(IsInternalConverter); - Debug.Assert(value != null); - - ReferenceResolver resolver = state.ReferenceResolver; - - // Write null to break reference cycles. - if (resolver.ContainsReferenceForCycleDetection(value)) - { - writer.WriteNullValue(); - return true; - } - // For boxed reference types: do not push when boxed in order to avoid false positives - // when we run the ContainsReferenceForCycleDetection check for the converter of the unboxed value. - Debug.Assert(!CanBePolymorphic); - resolver.PushReferenceForCycleDetection(value); - ignoreCyclesPopReference = true; - } - - if (CanBePolymorphic) - { - if (value == null) + if (options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.IgnoreCycles && + // .NET types that are serialized as JSON primitive values don't need to be tracked for cycle detection e.g: string. + ConverterStrategy != ConverterStrategy.Value) { - Debug.Assert(ConverterStrategy == ConverterStrategy.Value); - Debug.Assert(!state.IsContinuation); - Debug.Assert(HandleNullOnWrite); + // Custom (user) converters shall not track references + // it is responsibility of the user to break cycles in case there's any + // if we compare against Preserve, objects don't get preserved when a custom converter exists + // given that the custom converter executes prior to the preserve logic. + Debug.Assert(IsInternalConverter); - int originalPropertyDepth = writer.CurrentDepth; - Write(writer, value, options); - VerifyWrite(originalPropertyDepth, writer); + ReferenceResolver resolver = state.ReferenceResolver; - return true; - } + // Write null to break reference cycles. + if (resolver.ContainsReferenceForCycleDetection(value)) + { + writer.WriteNullValue(); + return true; + } - Type type = value.GetType(); - if (type == JsonTypeInfo.ObjectType) - { - writer.WriteStartObject(); - writer.WriteEndObject(); - return true; + // For boxed reference types: do not push when boxed in order to avoid false positives + // when we run the ContainsReferenceForCycleDetection check for the converter of the unboxed value. + Debug.Assert(!CanBePolymorphic); + resolver.PushReferenceForCycleDetection(value); + ignoreCyclesPopReference = true; } - if (type != TypeToConvert && IsInternalConverter) + if (CanBePolymorphic) { - // For internal converter only: Handle polymorphic case and get the new converter. - // Custom converter, even though polymorphic converter, get called for reading AND writing. - JsonConverter jsonConverter = state.Current.InitializeReEntry(type, options); - Debug.Assert(jsonConverter != this); - - if (options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.IgnoreCycles && - jsonConverter.IsValueType) + Type type = value.GetType(); + if (type == JsonTypeInfo.ObjectType) { - // For boxed value types: push the value before it gets unboxed on TryWriteAsObject. - state.ReferenceResolver.PushReferenceForCycleDetection(value); - ignoreCyclesPopReference = true; + writer.WriteStartObject(); + writer.WriteEndObject(); + return true; } - // We found a different converter; forward to that. - bool success2 = jsonConverter.TryWriteAsObject(writer, value, options, ref state); - - if (ignoreCyclesPopReference) + if (type != TypeToConvert && IsInternalConverter) { - state.ReferenceResolver.PopReferenceForCycleDetection(); - } + // For internal converter only: Handle polymorphic case and get the new converter. + // Custom converter, even though polymorphic converter, get called for reading AND writing. + JsonConverter jsonConverter = state.Current.InitializeReEntry(type, options); + Debug.Assert(jsonConverter != this); + + if (options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.IgnoreCycles && + jsonConverter.IsValueType) + { + // For boxed value types: push the value before it gets unboxed on TryWriteAsObject. + state.ReferenceResolver.PushReferenceForCycleDetection(value); + ignoreCyclesPopReference = true; + } + + // We found a different converter; forward to that. + bool success2 = jsonConverter.TryWriteAsObject(writer, value, options, ref state); - return success2; + if (ignoreCyclesPopReference) + { + state.ReferenceResolver.PopReferenceForCycleDetection(); + } + + return success2; + } } } @@ -416,7 +425,12 @@ internal bool TryWrite(Utf8JsonWriter writer, in T value, JsonSerializerOptions bool isContinuation = state.IsContinuation; +#if DEBUG + // DEBUG: ensure push/pop operations preserve stack integrity + JsonTypeInfo originalJsonTypeInfo = state.Current.JsonTypeInfo; +#endif state.Push(); + Debug.Assert(TypeToConvert.IsAssignableFrom(state.Current.JsonTypeInfo.Type)); if (!isContinuation) { @@ -432,6 +446,9 @@ internal bool TryWrite(Utf8JsonWriter writer, in T value, JsonSerializerOptions } state.Pop(success); +#if DEBUG + Debug.Assert(ReferenceEquals(originalJsonTypeInfo, state.Current.JsonTypeInfo)); +#endif if (ignoreCyclesPopReference) { @@ -476,6 +493,7 @@ internal bool TryWriteDataExtensionProperty(Utf8JsonWriter writer, T value, Json // Ignore the naming policy for extension data. state.Current.IgnoreDictionaryKeyPolicy = true; + state.Current.DeclaredJsonPropertyInfo = state.Current.JsonTypeInfo.ElementTypeInfo!.PropertyInfoForTypeInfo; success = dictionaryConverter.OnWriteResume(writer, value, options, ref state); if (success) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.String.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.String.cs index 71c30c512756c..db52ffc079ffe 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.String.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.String.cs @@ -125,9 +125,6 @@ private static string WriteUsingMetadata(in TValue value, JsonTypeInfo? throw new ArgumentNullException(nameof(jsonTypeInfo)); } - WriteStack state = default; - state.Initialize(jsonTypeInfo, supportContinuation: false); - JsonSerializerOptions options = jsonTypeInfo.Options; using (var output = new PooledByteBufferWriter(options.DefaultBufferSize)) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoOfT.cs index 911ee9f3ede5f..0f2ff8574f08d 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoOfT.cs @@ -234,11 +234,17 @@ internal override bool GetMemberAndWriteJson(object obj, ref WriteStack state, U { T value = Get!(obj); - if (Options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.IgnoreCycles && - value != null && + if ( +#if NET6_0_OR_GREATER + !typeof(T).IsValueType && // treated as a constant by recent versions of the JIT. +#else + !Converter.IsValueType && +#endif + Options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.IgnoreCycles && + value is not null && // .NET types that are serialized as JSON primitive values don't need to be tracked for cycle detection e.g: string. // However JsonConverter that uses ConverterStrategy == Value is an exception. - (Converter.CanBePolymorphic || (!Converter.IsValueType && ConverterStrategy != ConverterStrategy.Value)) && + (Converter.CanBePolymorphic || ConverterStrategy != ConverterStrategy.Value) && state.ReferenceResolver.ContainsReferenceForCycleDetection(value)) { // If a reference cycle is detected, treat value as null. diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadStack.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadStack.cs index 2f0347f68332a..c78646f45f4b1 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadStack.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadStack.cs @@ -16,16 +16,24 @@ internal struct ReadStack internal static readonly char[] SpecialCharacters = { '.', ' ', '\'', '/', '"', '[', ']', '(', ')', '\t', '\n', '\r', '\f', '\b', '\\', '\u0085', '\u2028', '\u2029' }; /// - /// The number of stack frames when the continuation started. + /// Exposes the stackframe that is currently active. /// - private int _continuationCount; + public ReadStackFrame Current; + + /// + /// Buffer containing all frames in the stack. For performance it is only populated for serialization depths > 1. + /// + private ReadStackFrame[] _stack; /// - /// The number of stack frames including Current. _previous will contain _count-1 higher frames. + /// Tracks the current depth of the stack. /// private int _count; - private List _previous; + /// + /// If not zero, indicates that the stack is part of a re-entrant continuation of given depth. + /// + private int _continuationCount; // State cache when deserializing objects with parameterized constructors. private List? _ctorArgStateCache; @@ -35,11 +43,10 @@ internal struct ReadStack /// public long BytesConsumed; - // A field is used instead of a property to avoid value semantics. - public ReadStackFrame Current; - + /// + /// Indicates that the state still contains suspended frames waiting re-entry. + /// public bool IsContinuation => _continuationCount != 0; - public bool IsLastContinuation => _continuationCount == _count; /// /// Internal flag to let us know that we need to read ahead in the inner read loop. @@ -59,25 +66,19 @@ internal struct ReadStack /// public bool UseFastPath; - private void AddCurrent() + /// + /// Ensures that the stack buffer has sufficient capacity to hold an additional frame. + /// + private void EnsurePushCapacity() { - if (_previous == null) - { - _previous = new List(); - } - - if (_count > _previous.Count) + if (_stack is null) { - // Need to allocate a new array element. - _previous.Add(Current); + _stack = new ReadStackFrame[4]; } - else + else if (_count - 1 == _stack.Length) { - // Use a previously allocated slot. - _previous[_count - 1] = Current; + Array.Resize(ref _stack, 2 * _stack.Length); } - - _count++; } public void Initialize(Type type, JsonSerializerOptions options, bool supportContinuation) @@ -143,8 +144,10 @@ public void Push() jsonTypeInfo = Current.JsonTypeInfo.ElementTypeInfo!; } - AddCurrent(); - Current.Reset(); + EnsurePushCapacity(); + _stack[_count - 1] = Current; + Current = default; + _count++; Current.JsonTypeInfo = jsonTypeInfo; Current.JsonPropertyInfo = jsonTypeInfo.PropertyInfoForTypeInfo; @@ -152,29 +155,26 @@ public void Push() Current.NumberHandling = numberHandling ?? Current.JsonPropertyInfo.NumberHandling; } } - else if (_continuationCount == 1) - { - // No need for a push since there is only one stack frame. - Debug.Assert(_count == 1); - _continuationCount = 0; - } else { - // A continuation; adjust the index. - Current = _previous[_count - 1]; - - // Check if we are done. - if (_count == _continuationCount) + // We are re-entering a continuation, adjust indices accordingly + if (_count++ > 0) { - _continuationCount = 0; + Current = _stack[_count - 1]; } - else + + // check if we are done + if (_continuationCount == _count) { - _count++; + _continuationCount = 0; } } SetConstructorArgumentState(); +#if DEBUG + // Ensure the method is always exercised in debug builds. + _ = JsonPath(); +#endif } public void Pop(bool success) @@ -188,41 +188,34 @@ public void Pop(bool success) { if (_count == 1) { - // No need for a continuation since there is only one stack frame. + // No need to copy any frames here. _continuationCount = 1; - } - else - { - AddCurrent(); - _count--; - _continuationCount = _count; - _count--; - Current = _previous[_count - 1]; + _count = 0; + return; } - return; + // Need to push the Current frame to the stack, + // ensure that we have sufficient capacity. + EnsurePushCapacity(); + _continuationCount = _count--; } - - if (_continuationCount == 1) + else if (--_count == 0) { - // No need for a pop since there is only one stack frame. - Debug.Assert(_count == 1); + // reached the root, no need to copy frames. return; } - // Update the list entry to the current value. - _previous[_count - 1] = Current; - - Debug.Assert(_count > 0); + _stack[_count] = Current; + Current = _stack[_count - 1]; } else { Debug.Assert(_continuationCount == 0); - } - if (_count > 1) - { - Current = _previous[--_count -1]; + if (--_count > 0) + { + Current = _stack[_count - 1]; + } } SetConstructorArgumentState(); @@ -240,26 +233,25 @@ public string JsonPath() for (int i = 0; i < count - 1; i++) { - AppendStackFrame(sb, _previous[i]); + AppendStackFrame(sb, ref _stack[i]); } if (_continuationCount == 0) { - AppendStackFrame(sb, Current); + AppendStackFrame(sb, ref Current); } return sb.ToString(); - static void AppendStackFrame(StringBuilder sb, in ReadStackFrame frame) + static void AppendStackFrame(StringBuilder sb, ref ReadStackFrame frame) { // Append the property name. - string? propertyName = GetPropertyName(frame); + string? propertyName = GetPropertyName(ref frame); AppendPropertyName(sb, propertyName); if (frame.JsonTypeInfo != null && frame.IsProcessingEnumerable()) { - IEnumerable? enumerable = (IEnumerable?)frame.ReturnValue; - if (enumerable == null) + if (frame.ReturnValue is not IEnumerable enumerable) { return; } @@ -276,7 +268,7 @@ static void AppendStackFrame(StringBuilder sb, in ReadStackFrame frame) } } - static int GetCount(IEnumerable enumerable) + static int GetCount(IEnumerable enumerable) { if (enumerable is ICollection collection) { @@ -311,7 +303,7 @@ static void AppendPropertyName(StringBuilder sb, string? propertyName) } } - static string? GetPropertyName(in ReadStackFrame frame) + static string? GetPropertyName(ref ReadStackFrame frame) { string? propertyName = null; @@ -350,10 +342,7 @@ private void SetConstructorArgumentState() // A zero index indicates a new stack frame. if (Current.CtorArgumentStateIndex == 0) { - if (_ctorArgStateCache == null) - { - _ctorArgStateCache = new List(); - } + _ctorArgStateCache ??= new List(); var newState = new ArgumentState(); _ctorArgStateCache.Add(newState); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs index f4f64dcbb1a51..137305f1d5b43 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs @@ -89,20 +89,5 @@ public bool IsProcessingEnumerable() { return (JsonTypeInfo.PropertyInfoForTypeInfo.ConverterStrategy & ConverterStrategy.Enumerable) != 0; } - - public void Reset() - { - CtorArgumentStateIndex = 0; - CtorArgumentState = null; - JsonTypeInfo = null!; - ObjectState = StackFrameObjectState.None; - OriginalDepth = 0; - OriginalTokenType = JsonTokenType.None; - PropertyIndex = 0; - PropertyRefCache = null; - ReturnValue = null; - - EndProperty(); - } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/WriteStack.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/WriteStack.cs index 22f67983ec346..e9a9f1e9e7546 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/WriteStack.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/WriteStack.cs @@ -16,15 +16,25 @@ namespace System.Text.Json internal struct WriteStack { /// - /// The number of stack frames when the continuation started. + /// Exposes the stackframe that is currently active. /// - private int _continuationCount; + public WriteStackFrame Current; + + /// + /// Buffer containing all frames in the stack. For performance it is only populated for serialization depths > 1. + /// + private WriteStackFrame[] _stack; /// - /// The number of stack frames including Current. _previous will contain _count-1 higher frames. + /// Tracks the current depth of the stack. /// private int _count; + /// + /// If not zero, indicates that the stack is part of a re-entrant continuation of given depth. + /// + private int _continuationCount; + /// /// Cancellation token used by converters performing async serialization (e.g. IAsyncEnumerable) /// @@ -41,17 +51,15 @@ internal struct WriteStack /// public List? PendingAsyncDisposables; - private List _previous; - - // A field is used instead of a property to avoid value semantics. - public WriteStackFrame Current; - /// /// The amount of bytes to write before the underlying Stream should be flushed and the /// current buffer adjusted to remove the processed bytes. /// public int FlushThreshold; + /// + /// Indicates that the state still contains suspended frames waiting re-entry. + /// public bool IsContinuation => _continuationCount != 0; // The bag of preservable references. @@ -62,25 +70,16 @@ internal struct WriteStack /// public bool SupportContinuation; - private void AddCurrent() + private void EnsurePushCapacity() { - if (_previous == null) + if (_stack is null) { - _previous = new List(); + _stack = new WriteStackFrame[4]; } - - if (_count > _previous.Count) + else if (_count - 1 == _stack.Length) { - // Need to allocate a new array element. - _previous.Add(Current); + Array.Resize(ref _stack, 2 * _stack.Length); } - else - { - // Use a previously allocated slot. - _previous[_count - 1] = Current; - } - - _count++; } /// @@ -125,8 +124,10 @@ public void Push() JsonTypeInfo jsonTypeInfo = Current.GetPolymorphicJsonPropertyInfo().RuntimeTypeInfo; JsonNumberHandling? numberHandling = Current.NumberHandling; - AddCurrent(); - Current.Reset(); + EnsurePushCapacity(); + _stack[_count - 1] = Current; + Current = default; + _count++; Current.JsonTypeInfo = jsonTypeInfo; Current.DeclaredJsonPropertyInfo = jsonTypeInfo.PropertyInfoForTypeInfo; @@ -134,27 +135,25 @@ public void Push() Current.NumberHandling = numberHandling ?? Current.DeclaredJsonPropertyInfo.NumberHandling; } } - else if (_continuationCount == 1) - { - // No need for a push since there is only one stack frame. - Debug.Assert(_count == 1); - _continuationCount = 0; - } else { - // A continuation, adjust the index. - Current = _previous[_count - 1]; - - // Check if we are done. - if (_count == _continuationCount) + // We are re-entering a continuation, adjust indices accordingly + if (_count++ > 0) { - _continuationCount = 0; + Current = _stack[_count - 1]; } - else + + // check if we are done + if (_continuationCount == _count) { - _count++; + _continuationCount = 0; } } + +#if DEBUG + // Ensure the method is always exercised in debug builds. + _ = PropertyPath(); +#endif } public void Pop(bool success) @@ -168,33 +167,25 @@ public void Pop(bool success) { if (_count == 1) { - // No need for a continuation since there is only one stack frame. + // No need to copy any frames here. _continuationCount = 1; - _count = 1; - } - else - { - AddCurrent(); - _count--; - _continuationCount = _count; - _count--; - Current = _previous[_count - 1]; + _count = 0; + return; } - return; + // Need to push the Current frame to the stack, + // ensure that we have sufficient capacity. + EnsurePushCapacity(); + _continuationCount = _count--; } - - if (_continuationCount == 1) + else if (--_count == 0) { - // No need for a pop since there is only one stack frame. - Debug.Assert(_count == 1); + // reached the root, no need to copy frames. return; } - // Update the list entry to the current value. - _previous[_count - 1] = Current; - - Debug.Assert(_count > 0); + _stack[_count] = Current; + Current = _stack[_count - 1]; } else { @@ -207,11 +198,11 @@ public void Pop(bool success) PendingAsyncDisposables ??= new List(); PendingAsyncDisposables.Add(Current.AsyncEnumerator); } - } - if (_count > 1) - { - Current = _previous[--_count - 1]; + if (--_count > 0) + { + Current = _stack[_count - 1]; + } } } @@ -253,13 +244,10 @@ public void DisposePendingDisposablesOnException() DisposeFrame(Current.CollectionEnumerator, ref exception); int stackSize = Math.Max(_count, _continuationCount); - if (stackSize > 1) + for (int i = 0; i < stackSize - 1; i++) { - for (int i = 0; i < stackSize - 1; i++) - { - Debug.Assert(_previous[i].AsyncEnumerator is null); - DisposeFrame(_previous[i].CollectionEnumerator, ref exception); - } + Debug.Assert(_stack[i].AsyncEnumerator is null); + DisposeFrame(_stack[i].CollectionEnumerator, ref exception); } if (exception is not null) @@ -294,12 +282,9 @@ public async ValueTask DisposePendingDisposablesOnExceptionAsync() exception = await DisposeFrame(Current.CollectionEnumerator, Current.AsyncEnumerator, exception).ConfigureAwait(false); int stackSize = Math.Max(_count, _continuationCount); - if (stackSize > 1) + for (int i = 0; i < stackSize - 1; i++) { - for (int i = 0; i < stackSize - 1; i++) - { - exception = await DisposeFrame(_previous[i].CollectionEnumerator, _previous[i].AsyncEnumerator, exception).ConfigureAwait(false); - } + exception = await DisposeFrame(_stack[i].CollectionEnumerator, _stack[i].AsyncEnumerator, exception).ConfigureAwait(false); } if (exception is not null) @@ -343,17 +328,17 @@ public string PropertyPath() for (int i = 0; i < count - 1; i++) { - AppendStackFrame(sb, _previous[i]); + AppendStackFrame(sb, ref _stack[i]); } if (_continuationCount == 0) { - AppendStackFrame(sb, Current); + AppendStackFrame(sb, ref Current); } return sb.ToString(); - void AppendStackFrame(StringBuilder sb, in WriteStackFrame frame) + static void AppendStackFrame(StringBuilder sb, ref WriteStackFrame frame) { // Append the property name. string? propertyName = frame.DeclaredJsonPropertyInfo?.MemberInfo?.Name; @@ -366,7 +351,7 @@ void AppendStackFrame(StringBuilder sb, in WriteStackFrame frame) AppendPropertyName(sb, propertyName); } - void AppendPropertyName(StringBuilder sb, string? propertyName) + static void AppendPropertyName(StringBuilder sb, string? propertyName) { if (propertyName != null) { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/WriteStackFrame.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/WriteStackFrame.cs index b162b55709c30..7d9ed3b6863f3 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/WriteStackFrame.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/WriteStackFrame.cs @@ -120,20 +120,5 @@ public JsonConverter InitializeReEntry(Type type, JsonSerializerOptions options) return PolymorphicJsonPropertyInfo.ConverterBase; } - - public void Reset() - { - CollectionEnumerator = null; - EnumeratorIndex = 0; - AsyncEnumerator = null; - AsyncEnumeratorIsPendingCompletion = false; - IgnoreDictionaryKeyPolicy = false; - JsonTypeInfo = null!; - OriginalDepth = 0; - ProcessedStartToken = false; - ProcessedEndToken = false; - - EndProperty(); - } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/ReferenceHandlerTests.IgnoreCycles.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/ReferenceHandlerTests.IgnoreCycles.cs index 8157f9bbdf3c9..235fa8010deb8 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/ReferenceHandlerTests.IgnoreCycles.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/ReferenceHandlerTests.IgnoreCycles.cs @@ -14,7 +14,7 @@ namespace System.Text.Json.Serialization.Tests public class ReferenceHandlerTests_IgnoreCycles { private static readonly JsonSerializerOptions s_optionsIgnoreCycles = - new JsonSerializerOptions { ReferenceHandler = ReferenceHandler.IgnoreCycles }; + new JsonSerializerOptions { ReferenceHandler = ReferenceHandler.IgnoreCycles, DefaultBufferSize = 1 }; [Fact] public async Task IgnoreCycles_OnObject() @@ -401,6 +401,25 @@ public async void IgnoreCycles_BoxedValueShouldNotBeIgnored() await Test_Serialize_And_SerializeAsync_Contains(root, expectedSubstring: @"""DayOfBirth"":15", expectedTimes: 2, s_optionsIgnoreCycles); } + [Fact] + public async Task CycleDetectionStatePersistsAcrossContinuations() + { + string expectedValueJson = @"{""LargePropertyName"":""A large-ish string to force continuations"",""Nested"":null}"; + var recVal = new RecursiveValue { LargePropertyName = "A large-ish string to force continuations" }; + recVal.Nested = recVal; + + var value = new List { recVal, recVal }; + string expectedJson = $"[{expectedValueJson},{expectedValueJson}]"; + + await Test_Serialize_And_SerializeAsync(value, expectedJson, s_optionsIgnoreCycles); + } + + public class RecursiveValue + { + public string LargePropertyName { get; set; } + public RecursiveValue? Nested { get; set; } + } + private async Task Test_Serialize_And_SerializeAsync(T obj, string expected, JsonSerializerOptions options) { string json; From 97477e250c1448431d4070fb4b324ebd3d7f1a4c Mon Sep 17 00:00:00 2001 From: Vladimir Sadov Date: Fri, 18 Jun 2021 13:48:22 -0700 Subject: [PATCH 020/107] Enable loading composite r2r images from a singlefile bundle (#53739) * Enable loading composite r2r images from a singlefile bundle --- src/coreclr/utilcode/pedecoder.cpp | 21 +++- src/coreclr/vm/nativeimage.cpp | 100 ++++++++++-------- .../BundledAppWithSubDirs.cs | 26 +++++ 3 files changed, 99 insertions(+), 48 deletions(-) diff --git a/src/coreclr/utilcode/pedecoder.cpp b/src/coreclr/utilcode/pedecoder.cpp index 3b46c56a41587..639cb02099bf4 100644 --- a/src/coreclr/utilcode/pedecoder.cpp +++ b/src/coreclr/utilcode/pedecoder.cpp @@ -1034,14 +1034,29 @@ CHECK PEDecoder::CheckCorHeader() const IMAGE_COR20_HEADER *pCor = GetCorHeader(); + // Currently composite r2r images miss some information, for example the version is 0.0. + // We may want to change that to something more conforming and explicit. + // For now, for compatibility purposes, we will accept that as a valid format. + bool possiblyCompositeR2R = + pCor->MinorRuntimeVersion == 0 && + pCor->MajorRuntimeVersion == 0; + //CHECK(((ULONGLONG)pCor & 0x3)==0); // If the file is COM+ 1.0, which by definition has nothing the runtime can // use, or if the file requires a newer version of this engine than us, // it cannot be run by this engine. - CHECK(VAL16(pCor->MajorRuntimeVersion) > 1 && VAL16(pCor->MajorRuntimeVersion) <= COR_VERSION_MAJOR); + if (!possiblyCompositeR2R) + CHECK(VAL16(pCor->MajorRuntimeVersion) > 1 && VAL16(pCor->MajorRuntimeVersion) <= COR_VERSION_MAJOR); +#ifdef HOST_WINDOWS CHECK(CheckDirectory(&pCor->MetaData, IMAGE_SCN_MEM_WRITE, HasNativeHeader() ? NULL_OK : NULL_NOT_OK)); +#else + CHECK(CheckDirectory( + &pCor->MetaData, + possiblyCompositeR2R ? 0 : IMAGE_SCN_MEM_WRITE, + HasNativeHeader() ? NULL_OK : NULL_NOT_OK)); +#endif CHECK(CheckDirectory(&pCor->Resources, IMAGE_SCN_MEM_WRITE, NULL_OK)); CHECK(CheckDirectory(&pCor->StrongNameSignature, IMAGE_SCN_MEM_WRITE, NULL_OK)); CHECK(CheckDirectory(&pCor->CodeManagerTable, IMAGE_SCN_MEM_WRITE, NULL_OK)); @@ -1083,7 +1098,7 @@ CHECK PEDecoder::CheckCorHeader() const // IL library files (really a misnomer - these are native images or ReadyToRun images) // only they can have a native image header - if ((pCor->Flags&VAL32(COMIMAGE_FLAGS_IL_LIBRARY)) == 0) + if ((pCor->Flags&VAL32(COMIMAGE_FLAGS_IL_LIBRARY)) == 0 && !possiblyCompositeR2R) { CHECK(VAL32(pCor->ManagedNativeHeader.Size) == 0); } @@ -1769,7 +1784,7 @@ void PEDecoder::LayoutILOnly(void *base, bool enableExecution) const PAGE_READONLY, &oldProtection)) ThrowLastError(); - // Finally, apply proper protection to copied sections + // Finally, apply proper protection to copied sections for (section = sectionStart; section < sectionEnd; section++) { // Add appropriate page protection. diff --git a/src/coreclr/vm/nativeimage.cpp b/src/coreclr/vm/nativeimage.cpp index 5500da1acc872..8338dbc60960b 100644 --- a/src/coreclr/vm/nativeimage.cpp +++ b/src/coreclr/vm/nativeimage.cpp @@ -143,59 +143,69 @@ NativeImage *NativeImage::Open( NewHolder peLoadedImage; - EX_TRY + BundleFileLocation bundleFileLocation = Bundle::ProbeAppBundle(fullPath, /*pathIsBundleRelative */ true); + if (bundleFileLocation.IsValid()) { - peLoadedImage = PEImageLayout::LoadNative(fullPath); + PEImageHolder pImage = PEImage::OpenImage(fullPath, MDInternalImport_NoCache, bundleFileLocation); + peLoadedImage = pImage->GetLayout(PEImageLayout::LAYOUT_MAPPED, PEImage::LAYOUT_CREATEIFNEEDED); } - EX_CATCH + + if (peLoadedImage.IsNull()) { - SString searchPaths(searchPathsConfig); - SString::CIterator start = searchPaths.Begin(); - while (start != searchPaths.End()) + EX_TRY { - SString::CIterator end = start; - if (!searchPaths.Find(end, PATH_SEPARATOR_CHAR_W)) - { - end = searchPaths.End(); - } - fullPath.Set(searchPaths, start, (COUNT_T)(end - start)); - - if (end != searchPaths.End()) - { - // Skip path separator character - ++end; - } - start = end; - - if (fullPath.GetCount() == 0) - { - continue; - } - - fullPath.Append(DIRECTORY_SEPARATOR_CHAR_W); - fullPath += compositeImageFileName; - - EX_TRY - { - peLoadedImage = PEImageLayout::LoadNative(fullPath); - break; - } - EX_CATCH + peLoadedImage = PEImageLayout::LoadNative(fullPath); + } + EX_CATCH + { + SString searchPaths(searchPathsConfig); + SString::CIterator start = searchPaths.Begin(); + while (start != searchPaths.End()) { + SString::CIterator end = start; + if (!searchPaths.Find(end, PATH_SEPARATOR_CHAR_W)) + { + end = searchPaths.End(); + } + fullPath.Set(searchPaths, start, (COUNT_T)(end - start)); + + if (end != searchPaths.End()) + { + // Skip path separator character + ++end; + } + start = end; + + if (fullPath.GetCount() == 0) + { + continue; + } + + fullPath.Append(DIRECTORY_SEPARATOR_CHAR_W); + fullPath += compositeImageFileName; + + EX_TRY + { + peLoadedImage = PEImageLayout::LoadNative(fullPath); + break; + } + EX_CATCH + { + } + EX_END_CATCH(SwallowAllExceptions) } - EX_END_CATCH(SwallowAllExceptions) } - } - EX_END_CATCH(SwallowAllExceptions) + EX_END_CATCH(SwallowAllExceptions) - if (peLoadedImage.IsNull()) - { - // Failed to locate the native composite R2R image - LOG((LF_LOADER, LL_ALWAYS, "LOADER: failed to load native image '%s' for component assembly '%S' using search paths: '%S'\n", - nativeImageFileName, - path.GetUnicode(), - searchPathsConfig != nullptr ? searchPathsConfig : W(""))); - RaiseFailFastException(nullptr, nullptr, 0); + if (peLoadedImage.IsNull()) + { + // Failed to locate the native composite R2R image + LOG((LF_LOADER, LL_ALWAYS, "LOADER: failed to load native image '%s' for component assembly '%S' using search paths: '%S'\n", + nativeImageFileName, + path.GetUnicode(), + searchPathsConfig != nullptr ? searchPathsConfig : W(""))); + RaiseFailFastException(nullptr, nullptr, 0); + } } READYTORUN_HEADER *pHeader = (READYTORUN_HEADER *)peLoadedImage->GetExport("RTR_HEADER"); diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs index 9ede20c516e39..6621259213815 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs @@ -126,6 +126,18 @@ public void Bundled_Self_Contained_Targeting50_WithCompression_Throws() Assert.Throws(()=>BundleHelper.BundleApp(fixture, options, new Version(5, 0))); } + [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54234")] + // NOTE: when enabling this test take a look at commented code maked by "ACTIVE ISSUE:" in SharedTestState + public void Bundled_Self_Contained_Composite_App_Run_Succeeds() + { + var fixture = sharedTestState.TestSelfContainedFixtureComposite.Copy(); + var singleFile = BundleSelfContainedApp(fixture, BundleOptions.None, disableCompression: true); + + // Run the app + RunTheApp(singleFile, fixture); + } + [InlineData(BundleOptions.None)] [InlineData(BundleOptions.BundleNativeBinaries)] [InlineData(BundleOptions.BundleAllContent)] @@ -144,6 +156,7 @@ public class SharedTestState : SharedTestStateBase, IDisposable public TestProjectFixture TestFrameworkDependentFixture { get; set; } public TestProjectFixture TestSelfContainedFixture { get; set; } public TestProjectFixture TestAppWithEmptyFileFixture { get; set; } + public TestProjectFixture TestSelfContainedFixtureComposite { get; set; } public SharedTestState() { @@ -169,6 +182,18 @@ public SharedTestState() .EnsureRestoredForRid(TestAppWithEmptyFileFixture.CurrentRid) .PublishProject(runtime: TestAppWithEmptyFileFixture.CurrentRid, outputDirectory: BundleHelper.GetPublishPath(TestAppWithEmptyFileFixture)); + + TestSelfContainedFixtureComposite = new TestProjectFixture("AppWithSubDirs", RepoDirectories); + BundleHelper.AddLongNameContentToAppWithSubDirs(TestSelfContainedFixtureComposite); + TestSelfContainedFixtureComposite + .EnsureRestoredForRid(TestSelfContainedFixtureComposite.CurrentRid) + .PublishProject(runtime: TestSelfContainedFixtureComposite.CurrentRid, + // ACTIVE ISSUE: https://github.com/dotnet/runtime/issues/54234 + // uncomment extraArgs when fixed. + outputDirectory: BundleHelper.GetPublishPath(TestSelfContainedFixtureComposite) /*, + extraArgs: new string[] { + "/p:PublishReadyToRun=true", + "/p:PublishReadyToRunComposite=true" } */); } public void Dispose() @@ -176,6 +201,7 @@ public void Dispose() TestFrameworkDependentFixture.Dispose(); TestSelfContainedFixture.Dispose(); TestAppWithEmptyFileFixture.Dispose(); + TestSelfContainedFixtureComposite.Dispose(); } } } From 8761cb9cf2201054409dd8e0bc2e6bfd85ae22bd Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Fri, 18 Jun 2021 18:40:07 -0400 Subject: [PATCH 021/107] [wasm][aot] Add support for passing items to the aot/helix test project (#54425) --- eng/testing/tests.wasm.targets | 13 +++- ...ix.proj => ProxyProjectForAOTOnHelix.proj} | 7 +- src/tasks/WasmBuildTasks/GenerateAOTProps.cs | 68 +++++++++++++++++-- 3 files changed, 77 insertions(+), 11 deletions(-) rename src/mono/wasm/data/aot-tests/{AOTTestProjectForHelix.proj => ProxyProjectForAOTOnHelix.proj} (85%) diff --git a/eng/testing/tests.wasm.targets b/eng/testing/tests.wasm.targets index ce10194388d55..1e1c86695796e 100644 --- a/eng/testing/tests.wasm.targets +++ b/eng/testing/tests.wasm.targets @@ -35,7 +35,7 @@ - <_AOTBuildCommand>dotnet msbuild publish/AOTTestProjectForHelix.proj /bl:$XHARNESS_OUT/AOTBuild.binlog + <_AOTBuildCommand>dotnet msbuild publish/ProxyProjectForAOTOnHelix.proj /bl:$XHARNESS_OUT/AOTBuild.binlog <_AOTBuildCommand Condition="'$(ContinuousIntegrationBuild)' != 'true'">$(_AOTBuildCommand) /p:RuntimeSrcDir=$(RepoRoot) /p:RuntimeConfig=$(Configuration) @@ -96,16 +96,23 @@ <_WasmPropertiesToPass Include="$(%(_WasmPropertyNames.Identity))" Name="%(_WasmPropertyNames.Identity)" - ConditionToUse="%(_WasmPropertyNames.ConditionToUse)" /> + ConditionToUse__="%(_WasmPropertyNames.ConditionToUse__)" /> <_WasmVFSFilesToCopy Include="@(WasmFilesToIncludeInFileSystem)" /> <_WasmVFSFilesToCopy TargetPath="%(FileName)%(Extension)" Condition="'%(TargetPath)' == ''" /> + + + Items="@(_WasmItemsToPass)" + OutputFile="$(BundleDir)publish\ProxyProjectForAOTOnHelix.props" /> diff --git a/src/mono/wasm/data/aot-tests/AOTTestProjectForHelix.proj b/src/mono/wasm/data/aot-tests/ProxyProjectForAOTOnHelix.proj similarity index 85% rename from src/mono/wasm/data/aot-tests/AOTTestProjectForHelix.proj rename to src/mono/wasm/data/aot-tests/ProxyProjectForAOTOnHelix.proj index f7b9d3fcc4fd0..0a0b915ad43ce 100644 --- a/src/mono/wasm/data/aot-tests/AOTTestProjectForHelix.proj +++ b/src/mono/wasm/data/aot-tests/ProxyProjectForAOTOnHelix.proj @@ -13,13 +13,18 @@ true false PrepareForWasmBuildApp;$(WasmBuildAppDependsOn) + + <_PropsFile>$(MSBuildThisFileDirectory)$(MSBuildThisFileName).props - + + + $(TestRootDir)AppBundle\ $(OriginalPublishDir)WasmTestRunner.dll diff --git a/src/tasks/WasmBuildTasks/GenerateAOTProps.cs b/src/tasks/WasmBuildTasks/GenerateAOTProps.cs index 8e6c9c3142a9b..83d2901a0f455 100644 --- a/src/tasks/WasmBuildTasks/GenerateAOTProps.cs +++ b/src/tasks/WasmBuildTasks/GenerateAOTProps.cs @@ -17,10 +17,35 @@ public class GenerateAOTProps : Task [Required] public ITaskItem[]? Properties { get; set; } + public ITaskItem[] Items { get; set; } = Array.Empty(); + [NotNull] [Required] public string? OutputFile { get; set; } + private const string s_originalItemNameMetadata = "OriginalItemName__"; + private const string s_conditionToUseMetadata = "ConditionToUse__"; + private static readonly HashSet s_metdataNamesToSkip = new() + { + "FullPath", + "RootDir", + "Filename", + "Extension", + "RelativeDir", + "Directory", + "RecursiveDir", + "Identity", + "ModifiedTime", + "CreatedTime", + "AccessedTime", + "DefiningProjectFullPath", + "DefiningProjectDirectory", + "DefiningProjectName", + "DefiningProjectExtension", + s_originalItemNameMetadata, + s_conditionToUseMetadata + }; + public override bool Execute() { var outDir = Path.GetDirectoryName(OutputFile); @@ -30,21 +55,50 @@ public override bool Execute() StringBuilder sb = new(); sb.AppendLine(""); - sb.AppendLine(" "); + sb.AppendLine("\t"); - foreach (var prop in Properties) + foreach (ITaskItem2 prop in Properties) { - string value = prop.ItemSpec; + string value = prop.EvaluatedIncludeEscaped; string? name = prop.GetMetadata("Name"); - string? condition = prop.GetMetadata("ConditionToUse"); + string? condition = prop.GetMetadataValueEscaped(s_conditionToUseMetadata); if (!string.IsNullOrEmpty(condition)) - sb.AppendLine($" <{name} Condition=\"{condition}\">{value}"); + sb.AppendLine($"\t\t<{name} Condition=\"{condition}\">{value}"); else - sb.AppendLine($" <{name}>{value}"); + sb.AppendLine($"\t\t<{name}>{value}"); + } + + sb.AppendLine("\t"); + + sb.AppendLine("\t"); + foreach (ITaskItem2 item in Items) + { + string value = item.EvaluatedIncludeEscaped; + string name = item.GetMetadata(s_originalItemNameMetadata); + + if (string.IsNullOrEmpty(name)) + { + Log.LogError($"Item {value} is missing {s_originalItemNameMetadata} metadata, for the item name"); + return false; + } + + sb.AppendLine($"\t\t<{name} Include=\"{value}\""); + + string? condition = item.GetMetadataValueEscaped(s_conditionToUseMetadata); + if (!string.IsNullOrEmpty(condition)) + sb.AppendLine($"\t\t\tCondition=\"{condition}\""); + + foreach (string mdName in item.MetadataNames) + { + if (!s_metdataNamesToSkip.Contains(mdName)) + sb.AppendLine($"\t\t\t{mdName}=\"{item.GetMetadataValueEscaped(mdName)}\""); + } + + sb.AppendLine($"\t\t/>"); } - sb.AppendLine(" "); + sb.AppendLine("\t"); sb.AppendLine(""); File.WriteAllText(OutputFile, sb.ToString()); From 27fece253494ac80f9f13843cbaa0e99a5d502ae Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Fri, 18 Jun 2021 16:48:58 -0700 Subject: [PATCH 022/107] Disable two Interop tests failing in outerloop under crossgen2 (#54435) https://github.com/dotnet/runtime/issues/54379 https://github.com/dotnet/runtime/issues/54316 --- src/tests/issues.targets | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 552b8b6400ffc..255236a9d346a 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -123,6 +123,12 @@ https://github.com/dotnet/runtime/issues/48727 + + https://github.com/dotnet/runtime/issues/54379 + + + https://github.com/dotnet/runtime/issues/54316 + From f5da499194580958cbaca9abdaf209572ec3b748 Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Fri, 18 Jun 2021 16:51:50 -0700 Subject: [PATCH 023/107] Fix AMD64 epilog ABI (#54357) * Fix AMD64 epilog ABI The Windows AMD64 epilog ABI states that an `lea rsp,[rbp+constant]` instruction may only be used if a frame pointer has been reported to the OS in the prolog unwind info, otherwise an `add rsp, constant` instruction must be used. There were a number of cases where the JIT used the `lea` form simply because a frame pointer had been established and was available, even though it had not been reported to the OS (and, thus, the frame was effectively an `rsp` frame). Fix this by using the same condition in the epilog for determining which form to use, `lea` or `add`, that was used in the prolog to determine whether or not to report the frame pointer in the unwind info. Fixes #54320 * Formatting * Fix OSR --- src/coreclr/jit/codegencommon.cpp | 37 +++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index f977ddfe6f31c..e99f047c061a5 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -7454,7 +7454,7 @@ void CodeGen::genFnProlog() // Establish the AMD64 frame pointer after the OS-reported prolog. if (doubleAlignOrFramePointerUsed()) { - bool reportUnwindData = compiler->compLocallocUsed || compiler->opts.compDbgEnC; + const bool reportUnwindData = compiler->compLocallocUsed || compiler->opts.compDbgEnC; genEstablishFramePointer(compiler->codeGen->genSPtoFPdelta(), reportUnwindData); } #endif // TARGET_AMD64 @@ -8147,7 +8147,31 @@ void CodeGen::genFnEpilog(BasicBlock* block) /* Compute the size in bytes we've pushed/popped */ - if (!doubleAlignOrFramePointerUsed()) + bool removeEbpFrame = doubleAlignOrFramePointerUsed(); + +#ifdef TARGET_AMD64 + // We only remove the EBP frame using the frame pointer (using `lea rsp, [rbp + const]`) + // if we reported the frame pointer in the prolog. The Windows x64 unwinding ABI specifically + // disallows this `lea` form: + // + // See https://docs.microsoft.com/en-us/cpp/build/prolog-and-epilog?view=msvc-160#epilog-code + // + // "When a frame pointer is not used, the epilog must use add RSP,constant to deallocate the fixed part of the + // stack. It may not use lea RSP,constant[RSP] instead. This restriction exists so the unwind code has fewer + // patterns to recognize when searching for epilogs." + // + // Otherwise, we must use `add RSP, constant`, as stated. So, we need to use the same condition + // as genFnProlog() used in determining whether to report the frame pointer in the unwind data. + // This is a subset of the `doubleAlignOrFramePointerUsed()` cases. + // + if (removeEbpFrame) + { + const bool reportUnwindData = compiler->compLocallocUsed || compiler->opts.compDbgEnC; + removeEbpFrame = removeEbpFrame && reportUnwindData; + } +#endif // TARGET_AMD64 + + if (!removeEbpFrame) { // We have an ESP frame */ @@ -8177,6 +8201,15 @@ void CodeGen::genFnEpilog(BasicBlock* block) genPopCalleeSavedRegisters(); +#ifdef TARGET_AMD64 + // In the case where we have an RSP frame, and no frame pointer reported in the OS unwind info, + // but we do have a pushed frame pointer and established frame chain, we do need to pop RBP. + if (doubleAlignOrFramePointerUsed()) + { + inst_RV(INS_pop, REG_EBP, TYP_I_IMPL); + } +#endif // TARGET_AMD64 + // Extra OSR adjust to get to where RBP was saved by the original frame, and // restore RBP. // From 3174651b1ed15a2cdd9789ac2282601925428e55 Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Fri, 18 Jun 2021 16:57:05 -0700 Subject: [PATCH 024/107] Remove crossgen from test build and run scripts (#54348) --- src/tests/build.cmd | 60 +++++++---------------- src/tests/build.sh | 37 ++------------ src/tests/run.cmd | 75 ----------------------------- src/tests/run.py | 115 +------------------------------------------- src/tests/run.sh | 17 ------- 5 files changed, 21 insertions(+), 283 deletions(-) diff --git a/src/tests/build.cmd b/src/tests/build.cmd index 18a85d6ae6221..cb8834ad0b021 100644 --- a/src/tests/build.cmd +++ b/src/tests/build.cmd @@ -47,9 +47,9 @@ set __SkipTestWrappers= set __BuildTestWrappersOnly= set __SkipNative= set __TargetsWindows=1 -set __DoCrossgen= set __DoCrossgen2= set __CompositeBuildMode= +set __TestBuildMode= set __CreatePdb= set __CopyNativeTestBinaries=0 set __CopyNativeProjectsAfterCombinedTestBuild=true @@ -100,7 +100,6 @@ if /i "%1" == "buildtestwrappersonly" (set __SkipNative=1&set __SkipManaged=1&se if /i "%1" == "-msbuild" (set __Ninja=0&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) if /i "%1" == "buildagainstpackages" (echo error: Remove /BuildAgainstPackages switch&&exit /b1) -if /i "%1" == "crossgen" (set __DoCrossgen=1&set __TestBuildMode=crossgen&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) if /i "%1" == "crossgen2" (set __DoCrossgen2=1&set __TestBuildMode=crossgen2&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) if /i "%1" == "composite" (set __CompositeBuildMode=1&set __DoCrossgen2=1&set __TestBuildMode=crossgen2&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) if /i "%1" == "pdb" (set __CreatePdb=1&shift&goto Arg_Loop) @@ -506,23 +505,7 @@ REM ============================================================================ if defined __SkipCrossgenFramework goto SkipCrossgen if defined __BuildTestWrappersOnly goto SkipCrossgen -set __CrossgenArg = "" -if defined __DoCrossgen ( - set __CrossgenArg="/p:Crossgen=true" - if "%__TargetsWindows%" == "1" ( - echo %__MsgPrefix%Running crossgen on framework assemblies in CORE_ROOT: %CORE_ROOT% - call :PrecompileFX - if ERRORLEVEL 1 ( - echo %__ErrMsgPrefix%%__MsgPrefix%Error: crossgen precompilation of framework assemblies failed - exit /b 1 - ) - ) else ( - echo "%__MsgPrefix%Crossgen only supported on Windows, for now" - ) -) - if defined __DoCrossgen2 ( - set __CrossgenArg="/p:Crossgen2=true" echo %__MsgPrefix%Running crossgen2 on framework assemblies in CORE_ROOT: %CORE_ROOT% call :PrecompileFX if ERRORLEVEL 1 ( @@ -555,14 +538,17 @@ echo. echo.-? -h -help --help: view this message. echo Build architecture: one of x64, x86, arm, arm64 ^(default: x64^). echo Build type: one of Debug, Checked, Release ^(default: Debug^). +echo skipgeneratelayout: Do not generate the Core_Root layout echo skipmanaged: skip the managed tests build echo skipnative: skip the native tests build echo skiprestorepackages: skip package restore -echo crossgen: Precompiles the framework managed assemblies +echo skiptestwrappers: skip generating test wrappers +echo buildtestwrappersonly: generate test wrappers without building managed or native test components or generating layouts echo copynativeonly: Only copy the native test binaries to the managed output. Do not build the native or managed tests. -echo skipgeneratelayout: Do not generate the Core_Root layout +echo crossgen2: Precompiles the framework managed assemblies +echo composite: Precompiles the framework managed assemblies in composite build mode +echo pdb: create PDB files when precompiling the framework managed assemblies echo generatelayoutonly: Generate the Core_Root layout without building managed or native test components -echo targetsNonWindows: echo Exclude- Optional parameter - specify location of default exclusion file ^(defaults to tests\issues.targets if not specified^) echo Set to "" to disable default exclusion file. echo -- ... : all arguments following this tag will be passed directly to msbuild. @@ -591,32 +577,20 @@ if defined __CompositeBuildMode ( ) set __CrossgenDir=%__BinDir% -if defined __DoCrossgen ( - if /i "%__BuildArch%" == "arm" ( - set __CrossgenDir=!__CrossgenDir!\x86 - ) - if /i "%__BuildArch%" == "arm64" ( - set __CrossgenDir=!__CrossgenDir!\x64 - ) - set __CrossgenCmd=%__CrossgenCmd% --crossgen --nocrossgen2 --crossgen-path "!__CrossgenDir!\crossgen.exe" -) else ( - if /i "%__BuildArch%" == "arm" ( - set __CrossgenDir=!__CrossgenDir!\x64 - ) - if /i "%__BuildArch%" == "arm64" ( - set __CrossgenDir=!__CrossgenDir!\x64 - ) - if /i "%__BuildArch%" == "x86" ( - set __CrossgenDir=!__CrossgenDir!\x64 - ) - set __CrossgenCmd=%__CrossgenCmd% --verify-type-and-field-layout --crossgen2-path "!__CrossgenDir!\crossgen2\crossgen2.dll" +if /i "%__BuildArch%" == "arm" ( + set __CrossgenDir=!__CrossgenDir!\x64 +) +if /i "%__BuildArch%" == "arm64" ( + set __CrossgenDir=!__CrossgenDir!\x64 ) +if /i "%__BuildArch%" == "x86" ( + set __CrossgenDir=!__CrossgenDir!\x64 +) +set __CrossgenCmd=%__CrossgenCmd% --verify-type-and-field-layout --crossgen2-path "!__CrossgenDir!\crossgen2\crossgen2.dll" echo Running %__CrossgenCmd% call %__CrossgenCmd% -set /a __exitCode = !errorlevel! - -if %__exitCode% neq 0 ( +if %errorlevel% neq 0 ( echo Failed to crossgen the framework exit /b 1 ) diff --git a/src/tests/build.sh b/src/tests/build.sh index 0d15d53b53910..da6fa03e1cfcf 100755 --- a/src/tests/build.sh +++ b/src/tests/build.sh @@ -133,8 +133,7 @@ generate_layout() build_MSBuild_projects "Tests_Overlay_Managed" "$__RepoRootDir/src/tests/run.proj" "Creating test overlay" "/t:CreateTestOverlay" # Precompile framework assemblies with crossgen if required - if [[ "$__DoCrossgen" != 0 || "$__DoCrossgen2" != 0 ]]; then - chmod +x "$__CrossgenExe" + if [[ "$__DoCrossgen2" != 0 ]]; then if [[ "$__SkipCrossgenFramework" == 0 ]]; then precompile_coreroot_fx fi @@ -171,11 +170,7 @@ precompile_coreroot_fx() crossgenDir="$crossgenDir/$__HostArch" fi - if [[ "$__DoCrossgen" != 0 ]]; then - crossgenCmd="$crossgenCmd --crossgen --nocrossgen2 --crossgen-path \"$crossgenDir/crossgen\"" - else - crossgenCmd="$crossgenCmd --verify-type-and-field-layout --crossgen2-path \"$crossgenDir/crossgen2/crossgen2.dll\"" - fi + crossgenCmd="$crossgenCmd --verify-type-and-field-layout --crossgen2-path \"$crossgenDir/crossgen2/crossgen2.dll\"" echo "Running $crossgenCmd" eval $crossgenCmd @@ -191,17 +186,6 @@ precompile_coreroot_fx() return 0 } -declare -a skipCrossGenFiles - -function is_skip_crossgen_test { - for skip in "${skipCrossGenFiles[@]}"; do - if [[ "$1" == "$skip" ]]; then - return 0 - fi - done - return 1 -} - build_Tests() { echo "${__MsgPrefix}Building Tests..." @@ -447,7 +431,6 @@ usage_list+=("-buildtestwrappersonly: only build the test wrappers.") usage_list+=("-copynativeonly: Only copy the native test binaries to the managed output. Do not build the native or managed tests.") usage_list+=("-generatelayoutonly: only pull down dependencies and build coreroot.") -usage_list+=("-crossgen: Precompiles the framework managed assemblies in coreroot.") usage_list+=("-crossgen2: Precompiles the framework managed assemblies in coreroot using the Crossgen2 compiler.") usage_list+=("-priority1: include priority=1 tests in the build.") usage_list+=("-allTargets: Build managed tests for all target platforms.") @@ -480,11 +463,6 @@ handle_arguments_local() { __SkipCrossgenFramework=1 ;; - crossgen|-crossgen) - __DoCrossgen=1 - __TestBuildMode=crossgen - ;; - crossgen2|-crossgen2) __DoCrossgen2=1 __TestBuildMode=crossgen2 @@ -559,9 +537,9 @@ __CopyNativeProjectsAfterCombinedTestBuild=true __CopyNativeTestBinaries=0 __CrossBuild=0 __DistroRid="" -__DoCrossgen=0 __DoCrossgen2=0 __CompositeBuildMode=0 +__TestBuildMode= __DotNetCli="$__RepoRootDir/dotnet.sh" __GenerateLayoutOnly= __IsMSBuildOnNETCoreSupported=0 @@ -607,18 +585,9 @@ __TestDir="$__RepoRootDir/src/tests" __TestWorkingDir="$__RootBinDir/tests/coreclr/$__OSPlatformConfig" __IntermediatesDir="$__RootBinDir/obj/coreclr/$__OSPlatformConfig" __TestIntermediatesDir="$__RootBinDir/tests/coreclr/obj/$__OSPlatformConfig" -__CrossComponentBinDir="$__BinDir" __CrossCompIntermediatesDir="$__IntermediatesDir/crossgen" __MonoBinDir="$__RootBinDir/bin/mono/$__OSPlatformConfig" -__CrossArch="$__HostArch" -if [[ "$__CrossBuild" == 1 ]]; then - __CrossComponentBinDir="$__CrossComponentBinDir/$__CrossArch" -fi -__CrossgenCoreLibLog="$__LogsDir/CrossgenCoreLib_$__TargetOS.$BuildArch.$__BuildType.log" -__CrossgenExe="$__CrossComponentBinDir/crossgen" -__Crossgen2Dll="$__CrossComponentBinDir/crossgen2/crossgen2.dll" - # CI_SPECIFIC - On CI machines, $HOME may not be set. In such a case, create a subfolder and set the variable to it. # This is needed by CLI to function. if [[ -z "$HOME" ]]; then diff --git a/src/tests/run.cmd b/src/tests/run.cmd index 98f90f3344a70..3f1e762df09e9 100644 --- a/src/tests/run.cmd +++ b/src/tests/run.cmd @@ -26,8 +26,6 @@ set __msbuildExtraArgs= set __LongGCTests= set __GCSimulatorTests= set __IlasmRoundTrip= -set __DoCrossgen= -set __CrossgenAltJit= set __PrintLastResultsOnly= set RunInUnloadableContext= @@ -50,13 +48,8 @@ if /i "%1" == "debug" (set __BuildType=Debug&s if /i "%1" == "release" (set __BuildType=Release&shift&goto Arg_Loop) if /i "%1" == "checked" (set __BuildType=Checked&shift&goto Arg_Loop) -if /i "%1" == "vs2017" (set __VSVersion=%1&shift&goto Arg_Loop) -if /i "%1" == "vs2019" (set __VSVersion=%1&shift&goto Arg_Loop) - if /i "%1" == "TestEnv" (set __TestEnv=%2&shift&shift&goto Arg_Loop) if /i "%1" == "sequential" (set __Sequential=1&shift&goto Arg_Loop) -if /i "%1" == "crossgen" (set __DoCrossgen=1&shift&goto Arg_Loop) -if /i "%1" == "crossgenaltjit" (set __DoCrossgen=1&set __CrossgenAltJit=%2&shift&shift&goto Arg_Loop) if /i "%1" == "longgc" (set __LongGCTests=1&shift&goto Arg_Loop) if /i "%1" == "gcsimulator" (set __GCSimulatorTests=1&shift&goto Arg_Loop) if /i "%1" == "jitstress" (set COMPlus_JitStress=%2&shift&shift&goto Arg_Loop) @@ -66,10 +59,8 @@ if /i "%1" == "jitforcerelocs" (set COMPlus_ForceRelocs if /i "%1" == "ilasmroundtrip" (set __IlasmRoundTrip=1&shift&goto Arg_Loop) if /i "%1" == "printlastresultsonly" (set __PrintLastResultsOnly=1&shift&goto Arg_Loop) -if /i "%1" == "runcrossgentests" (set RunCrossGen=true&shift&goto Arg_Loop) if /i "%1" == "runcrossgen2tests" (set RunCrossGen2=true&shift&goto Arg_Loop) REM This test feature is currently intentionally undocumented -if /i "%1" == "runlargeversionbubblecrossgentests" (set RunCrossGen=true&set CrossgenLargeVersionBubble=true&shift&goto Arg_Loop) if /i "%1" == "runlargeversionbubblecrossgen2tests" (set RunCrossGen2=true&set CrossgenLargeVersionBubble=true&shift&goto Arg_Loop) if /i "%1" == "link" (set DoLink=true&set ILLINK=%2&shift&shift&goto Arg_Loop) if /i "%1" == "gcname" (set COMPlus_GCName=%2&shift&shift&goto Arg_Loop) @@ -138,18 +129,10 @@ if defined __Sequential ( set __RuntestPyArgs=%__RuntestPyArgs% --sequential ) -if defined RunCrossGen ( - set __RuntestPyArgs=%__RuntestPyArgs% --run_crossgen_tests -) - if defined RunCrossGen2 ( set __RuntestPyArgs=%__RuntestPyArgs% --run_crossgen2_tests ) -if defined __DoCrossgen ( - set __RuntestPyArgs=%__RuntestPyArgs% --precompile_core_root -) - if defined CrossgenLargeVersionBubble ( set __RuntestPyArgs=%__RuntestPyArgs% --large_version_bubble ) @@ -242,14 +225,6 @@ call :SetTestEnvironment call :ResolveDependencies if errorlevel 1 exit /b 1 -if defined __DoCrossgen ( - echo %__MsgPrefix%Running crossgen on framework assemblies - call :PrecompileFX -) - -REM Delete the unecessary mscorlib.ni file. -if exist %CORE_ROOT%\mscorlib.ni.dll del %CORE_ROOT%\mscorlib.ni.dll - ::Check if the test Binaries are built if not exist %XunitTestBinBase% ( echo %__MsgPrefix%Error: Ensure the Test Binaries are built and are present at %XunitTestBinBase%. @@ -286,52 +261,6 @@ echo %__TestRunHtmlLog% echo %__TestRunXmlLog% exit /b 0 -REM ========================================================================================= -REM === -REM === Compile the managed assemblies in Core_ROOT before running the tests -REM === -REM ========================================================================================= - -:PrecompileAssembly - -REM Skip mscorlib since it is already precompiled. -if /I "%3" == "mscorlib.dll" exit /b 0 -if /I "%3" == "mscorlib.ni.dll" exit /b 0 - -"%1\crossgen.exe" /nologo /Platform_Assemblies_Paths "%CORE_ROOT%" "%2" >nul 2>nul -set /a __exitCode = %errorlevel% -if "%__exitCode%" == "-2146230517" ( - echo %2 is not a managed assembly. - exit /b 0 -) - -if %__exitCode% neq 0 ( - echo Unable to precompile %2 - exit /b 0 -) - -echo %__MsgPrefix%Successfully precompiled %2 -exit /b 0 - -:PrecompileFX -setlocal - -if defined __CrossgenAltJit ( - REM Set altjit flags for the crossgen run. Note that this entire crossgen section is within a setlocal/endlocal scope, - REM so we don't need to save or unset these afterwards. - echo %__MsgPrefix%Setting altjit environment variables for %__CrossgenAltJit%. - set COMPlus_AltJit=* - set COMPlus_AltJitNgen=* - set COMPlus_AltJitName=%__CrossgenAltJit% - set COMPlus_AltJitAssertOnNYI=1 - set COMPlus_NoGuiOnAssert=1 - set COMPlus_ContinueOnAssert=0 -) - -for %%F in (%CORE_ROOT%\*.dll) do call :PrecompileAssembly "%CORE_ROOT%" "%%F" %%~nF%%~xF -endlocal -exit /b 0 - REM ========================================================================================= REM === REM === Subroutine to invoke msbuild. @@ -447,12 +376,8 @@ echo. echo./? -? /h -h /help -help - View this message. echo ^ - Specifies build architecture: x64, x86, arm, or arm64 ^(default: x64^). echo ^ - Specifies build type: Debug, Release, or Checked ^(default: Debug^). -echo VSVersion ^ - VS2017 or VS2019 ^(default: VS2019^). echo TestEnv ^ - Run a custom script before every test to set custom test environment settings. echo sequential - Run tests sequentially (no parallelism). -echo crossgen - Precompile ^(crossgen^) the managed assemblies in CORE_ROOT before running the tests. -echo crossgenaltjit ^ - Precompile ^(crossgen^) the managed assemblies in CORE_ROOT before running the tests, using the given altjit. -echo RunCrossgenTests - Runs ReadytoRun tests echo RunCrossgen2Tests - Runs ReadytoRun tests compiled with Crossgen2 echo jitstress ^ - Runs the tests with COMPlus_JitStress=n echo jitstressregs ^ - Runs the tests with COMPlus_JitStressRegs=n diff --git a/src/tests/run.py b/src/tests/run.py index 8de44fefeffb4..c6750a248bded 100755 --- a/src/tests/run.py +++ b/src/tests/run.py @@ -84,7 +84,6 @@ parser.add_argument("-core_root", dest="core_root", nargs='?', default=None) parser.add_argument("-runtime_repo_location", dest="runtime_repo_location", default=os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))) parser.add_argument("-test_env", dest="test_env", default=None) -parser.add_argument("-crossgen_altjit", dest="crossgen_altjit", default=None) # Optional arguments which change execution. @@ -92,11 +91,9 @@ parser.add_argument("--long_gc", dest="long_gc", action="store_true", default=False) parser.add_argument("--gcsimulator", dest="gcsimulator", action="store_true", default=False) parser.add_argument("--ilasmroundtrip", dest="ilasmroundtrip", action="store_true", default=False) -parser.add_argument("--run_crossgen_tests", dest="run_crossgen_tests", action="store_true", default=False) parser.add_argument("--run_crossgen2_tests", dest="run_crossgen2_tests", action="store_true", default=False) parser.add_argument("--large_version_bubble", dest="large_version_bubble", action="store_true", default=False) -parser.add_argument("--precompile_core_root", dest="precompile_core_root", action="store_true", default=False) -parser.add_argument("--skip_test_run", dest="skip_test_run", action="store_true", default=False, help="Does not run tests. Useful in conjunction with --precompile_core_root") +parser.add_argument("--skip_test_run", dest="skip_test_run", action="store_true", default=False, help="Does not run tests.") parser.add_argument("--sequential", dest="sequential", action="store_true", default=False) parser.add_argument("--analyze_results_only", dest="analyze_results_only", action="store_true", default=False) @@ -842,9 +839,6 @@ def run_tests(args, test_env_script_path : Path to script to use to set the test environment, if any. """ - if args.precompile_core_root: - precompile_core_root(args) - if args.skip_test_run: return @@ -869,11 +863,6 @@ def run_tests(args, print("Setting RunningIlasmRoundTrip=1") os.environ["RunningIlasmRoundTrip"] = "1" - if args.run_crossgen_tests: - print("Running tests R2R") - print("Setting RunCrossGen=true") - os.environ["RunCrossGen"] = "true" - if args.run_crossgen2_tests: print("Running tests R2R (Crossgen2)") print("Setting RunCrossGen2=true") @@ -962,11 +951,6 @@ def setup_args(args): lambda arg: True, "Error setting analyze_results_only") - coreclr_setup_args.verify(args, - "crossgen_altjit", - lambda arg: True, - "Error setting crossgen_altjit") - coreclr_setup_args.verify(args, "rid", lambda arg: True, @@ -997,21 +981,11 @@ def setup_args(args): lambda arg: True, "Error setting large_version_bubble") - coreclr_setup_args.verify(args, - "run_crossgen_tests", - lambda arg: True, - "Error setting run_crossgen_tests") - coreclr_setup_args.verify(args, "run_crossgen2_tests", lambda unused: True, "Error setting run_crossgen2_tests") - coreclr_setup_args.verify(args, - "precompile_core_root", - lambda arg: True, - "Error setting precompile_core_root") - coreclr_setup_args.verify(args, "skip_test_run", lambda arg: True, @@ -1044,7 +1018,6 @@ def setup_args(args): print("core_root : %s" % coreclr_setup_args.core_root) print("test_location : %s" % coreclr_setup_args.test_location) - coreclr_setup_args.crossgen_path = os.path.join(coreclr_setup_args.core_root, "crossgen%s" % (".exe" if coreclr_setup_args.host_os == "windows" else "")) coreclr_setup_args.corerun_path = os.path.join(coreclr_setup_args.core_root, "corerun%s" % (".exe" if coreclr_setup_args.host_os == "windows" else "")) coreclr_setup_args.dotnetcli_script_path = os.path.join(coreclr_setup_args.runtime_repo_location, "dotnet%s" % (".cmd" if coreclr_setup_args.host_os == "windows" else ".sh")) coreclr_setup_args.coreclr_tests_dir = os.path.join(coreclr_setup_args.coreclr_dir, "tests") @@ -1054,92 +1027,6 @@ def setup_args(args): return coreclr_setup_args -def precompile_core_root(args): - """ Precompile all of the assemblies in the core_root directory - - Args: - args - """ - - skip_list = [ - ".*xunit.*", - ".*api-ms-win-core.*", - ".*api-ms-win.*", - ".*System.Private.CoreLib.*" - ] - - unix_skip_list = [ - ".*mscorlib.*", - ".*System.Runtime.WindowsRuntime.*", - ".*System.Runtime.WindowsRuntime.UI.Xaml.*", - ".*R2RDump.dll.*" - ] - - arm64_unix_skip_list = [ - ".*Microsoft.CodeAnalysis.VisualBasic.*", - ".*System.Net.NameResolution.*", - ".*System.Net.Sockets.*", - ".*System.Net.Primitives.*" - ] - - if args.host_os != "windows": - skip_list += unix_skip_list - - if args.arch == "arm64": - skip_list += arm64_unix_skip_list - - assert os.path.isdir(args.test_location) - assert os.path.isdir(args.core_root) - - def call_crossgen(file, env): - assert os.path.isfile(args.crossgen_path) - command = [args.crossgen_path, "/Platform_Assemblies_Paths", args.core_root, file] - - proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) - proc.communicate() - - return_code = proc.returncode - - if return_code == -2146230517: - print("%s is not a managed assembly." % file) - return False - - if return_code != 0: - print("Unable to precompile %s (%d)" % (file, return_code)) - return False - - print("Successfully precompiled %s" % file) - return True - - print("Precompiling all assemblies in %s" % args.core_root) - print("") - - env = os.environ.copy() - - if not args.crossgen_altjit is None: - env["COMPlus_AltJit"]="*" - env["COMPlus_AltJitNgen"]="*" - env["COMPlus_AltJitName"]=args.crossgen_altjit - env["COMPlus_AltJitAssertOnNYI"]="1" - env["COMPlus_NoGuiOnAssert"]="1" - env["COMPlus_ContinueOnAssert"]="0" - - dlls = [os.path.join(args.core_root, item) for item in os.listdir(args.core_root) if item.endswith("dll") and "mscorlib" not in item] - - def in_skip_list(item): - found = False - for skip_re in skip_list: - if re.match(skip_re, item.lower()) is not None: - found = True - return found - - dlls = [dll for dll in dlls if not in_skip_list(dll)] - - for dll in dlls: - call_crossgen(dll, env) - - print("") - if sys.version_info.major < 3: def to_unicode(s): return unicode(s, "utf-8") diff --git a/src/tests/run.sh b/src/tests/run.sh index bfa564d4cee6e..362d42027f202 100755 --- a/src/tests/run.sh +++ b/src/tests/run.sh @@ -17,8 +17,6 @@ function print_usage { echo ' --testRootDir= : Root directory of the test build (e.g. runtime/artifacts/tests/windows.x64.Debug).' echo ' --disableEventLogging : Disable the events logged by both VM and Managed Code' echo ' --sequential : Run tests sequentially (default is to run in parallel).' - echo ' --crossgen : Precompiles the framework managed assemblies' - echo ' --runcrossgentests : Runs the ReadyToRun tests' echo ' --runcrossgen2tests : Runs the ReadyToRun tests compiled with Crossgen2' echo ' --jitstress= : Runs the tests with COMPlus_JitStress=n' echo ' --jitstressregs= : Runs the tests with COMPlus_JitStressRegs=n' @@ -94,7 +92,6 @@ limitedCoreDumps= # Handle arguments verbose=0 -doCrossgen=0 ilasmroundtrip= printLastResultsOnly= runSequential=0 @@ -140,9 +137,6 @@ do --printLastResultsOnly) printLastResultsOnly=1 ;; - --crossgen) - doCrossgen=1 - ;; --jitstress=*) export COMPlus_JitStress=${i#*=} ;; @@ -168,9 +162,6 @@ do --disableEventLogging) ((disableEventLogging = 1)) ;; - --runcrossgentests) - export RunCrossGen=1 - ;; --runcrossgen2tests) export RunCrossGen2=1 ;; @@ -281,18 +272,10 @@ if [ ! -z "$printLastResultsOnly" ]; then runtestPyArguments+=("--analyze_results_only") fi -if [ ! -z "$RunCrossGen" ]; then - runtestPyArguments+=("--run_crossgen_tests") -fi - if [ ! -z "$RunCrossGen2" ]; then runtestPyArguments+=("--run_crossgen2_tests") fi -if (($doCrossgen!=0)); then - runtestPyArguments+=("--precompile_core_root") -fi - if [ "$limitedCoreDumps" == "ON" ]; then runtestPyArguments+=("--limited_core_dumps") fi From ca3c11d64516217134430d04354ac770b07c91ef Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Sat, 19 Jun 2021 02:37:42 +0200 Subject: [PATCH 025/107] enable ZeroByteRead_BlocksUntilDataAvailableOrNops test for quic (#54431) --- .../QuicStreamConnectedStreamConformanceTests.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs index 35b54bc6d70b8..cd83662a1baa9 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs @@ -22,6 +22,7 @@ public sealed class MsQuicQuicStreamConformanceTests : QuicStreamConformanceTest { protected override QuicImplementationProvider Provider => QuicImplementationProviders.MsQuic; protected override bool UsableAfterCanceledReads => false; + protected override bool BlocksOnZeroByteReads => true; // TODO: These are all hanging, likely due to Stream close behavior. [ActiveIssue("https://github.com/dotnet/runtime/issues/756")] @@ -43,8 +44,6 @@ public sealed class MsQuicQuicStreamConformanceTests : QuicStreamConformanceTest [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] public override Task Read_DataStoredAtDesiredOffset(ReadWriteMode mode) => base.Read_DataStoredAtDesiredOffset(mode); [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] - public override Task ZeroByteRead_BlocksUntilDataAvailableOrNops(ReadWriteMode mode) => base.ZeroByteRead_BlocksUntilDataAvailableOrNops(mode); - [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] public override Task ReadAsync_DuringReadAsync_ThrowsIfUnsupported() => base.ReadAsync_DuringReadAsync_ThrowsIfUnsupported(); } From 9c5581c09e2c90450d644c9215493f2131515da1 Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Sat, 19 Jun 2021 10:07:59 -0700 Subject: [PATCH 026/107] Disable tests for #54007 (#54207) --- src/libraries/System.IO.Hashing/tests/Crc32Tests.cs | 1 + src/libraries/System.IO.Hashing/tests/Crc64Tests.cs | 1 + src/libraries/System.IO.Hashing/tests/XxHash32Tests.007.cs | 1 + src/libraries/System.IO.Hashing/tests/XxHash32Tests.cs | 1 + src/libraries/System.IO.Hashing/tests/XxHash32Tests.f00d.cs | 1 + src/libraries/System.IO.Hashing/tests/XxHash64Tests.007.cs | 1 + src/libraries/System.IO.Hashing/tests/XxHash64Tests.cs | 1 + src/libraries/System.IO.Hashing/tests/XxHash64Tests.f00d.cs | 1 + 8 files changed, 8 insertions(+) diff --git a/src/libraries/System.IO.Hashing/tests/Crc32Tests.cs b/src/libraries/System.IO.Hashing/tests/Crc32Tests.cs index 6d2993f1509e4..4e5ac8753d3f7 100644 --- a/src/libraries/System.IO.Hashing/tests/Crc32Tests.cs +++ b/src/libraries/System.IO.Hashing/tests/Crc32Tests.cs @@ -93,6 +93,7 @@ public void InstanceAppendAllocateAndReset(TestCase testCase) InstanceAppendAllocateAndResetDriver(testCase); } + [SkipOnCoreClr("https://github.com/dotnet/runtime/issues/54007", RuntimeConfiguration.Checked)] [Theory] [MemberData(nameof(TestCases))] public void InstanceMultiAppendGetCurrentHash(TestCase testCase) diff --git a/src/libraries/System.IO.Hashing/tests/Crc64Tests.cs b/src/libraries/System.IO.Hashing/tests/Crc64Tests.cs index d647e4b958798..546501aa4d545 100644 --- a/src/libraries/System.IO.Hashing/tests/Crc64Tests.cs +++ b/src/libraries/System.IO.Hashing/tests/Crc64Tests.cs @@ -97,6 +97,7 @@ public void InstanceAppendAllocateAndReset(TestCase testCase) InstanceAppendAllocateAndResetDriver(testCase); } + [SkipOnCoreClr("https://github.com/dotnet/runtime/issues/54007", RuntimeConfiguration.Checked)] [Theory] [MemberData(nameof(TestCases))] public void InstanceMultiAppendGetCurrentHash(TestCase testCase) diff --git a/src/libraries/System.IO.Hashing/tests/XxHash32Tests.007.cs b/src/libraries/System.IO.Hashing/tests/XxHash32Tests.007.cs index eed5aec365b3e..cf2e967d32a68 100644 --- a/src/libraries/System.IO.Hashing/tests/XxHash32Tests.007.cs +++ b/src/libraries/System.IO.Hashing/tests/XxHash32Tests.007.cs @@ -105,6 +105,7 @@ public void InstanceAppendAllocateAndReset(TestCase testCase) InstanceAppendAllocateAndResetDriver(testCase); } + [SkipOnCoreClr("https://github.com/dotnet/runtime/issues/54007", RuntimeConfiguration.Checked)] [Theory] [MemberData(nameof(TestCases))] public void InstanceMultiAppendGetCurrentHash(TestCase testCase) diff --git a/src/libraries/System.IO.Hashing/tests/XxHash32Tests.cs b/src/libraries/System.IO.Hashing/tests/XxHash32Tests.cs index f9dccad029402..15c671d12bee9 100644 --- a/src/libraries/System.IO.Hashing/tests/XxHash32Tests.cs +++ b/src/libraries/System.IO.Hashing/tests/XxHash32Tests.cs @@ -118,6 +118,7 @@ public void InstanceAppendAllocateAndReset(TestCase testCase) InstanceAppendAllocateAndResetDriver(testCase); } + [SkipOnCoreClr("https://github.com/dotnet/runtime/issues/54007", RuntimeConfiguration.Checked)] [Theory] [MemberData(nameof(TestCases))] public void InstanceMultiAppendGetCurrentHash(TestCase testCase) diff --git a/src/libraries/System.IO.Hashing/tests/XxHash32Tests.f00d.cs b/src/libraries/System.IO.Hashing/tests/XxHash32Tests.f00d.cs index 5171e62f8ca56..007acd7655f55 100644 --- a/src/libraries/System.IO.Hashing/tests/XxHash32Tests.f00d.cs +++ b/src/libraries/System.IO.Hashing/tests/XxHash32Tests.f00d.cs @@ -105,6 +105,7 @@ public void InstanceAppendAllocateAndReset(TestCase testCase) InstanceAppendAllocateAndResetDriver(testCase); } + [SkipOnCoreClr("https://github.com/dotnet/runtime/issues/54007", RuntimeConfiguration.Checked)] [Theory] [MemberData(nameof(TestCases))] public void InstanceMultiAppendGetCurrentHash(TestCase testCase) diff --git a/src/libraries/System.IO.Hashing/tests/XxHash64Tests.007.cs b/src/libraries/System.IO.Hashing/tests/XxHash64Tests.007.cs index 5c2e575377870..4da264fb5ff10 100644 --- a/src/libraries/System.IO.Hashing/tests/XxHash64Tests.007.cs +++ b/src/libraries/System.IO.Hashing/tests/XxHash64Tests.007.cs @@ -115,6 +115,7 @@ public void InstanceAppendAllocateAndReset(TestCase testCase) InstanceAppendAllocateAndResetDriver(testCase); } + [SkipOnCoreClr("https://github.com/dotnet/runtime/issues/54007", RuntimeConfiguration.Checked)] [Theory] [MemberData(nameof(TestCases))] public void InstanceMultiAppendGetCurrentHash(TestCase testCase) diff --git a/src/libraries/System.IO.Hashing/tests/XxHash64Tests.cs b/src/libraries/System.IO.Hashing/tests/XxHash64Tests.cs index 973d108fc9377..39245a975d878 100644 --- a/src/libraries/System.IO.Hashing/tests/XxHash64Tests.cs +++ b/src/libraries/System.IO.Hashing/tests/XxHash64Tests.cs @@ -131,6 +131,7 @@ public void InstanceAppendAllocateAndReset(TestCase testCase) InstanceAppendAllocateAndResetDriver(testCase); } + [SkipOnCoreClr("https://github.com/dotnet/runtime/issues/54007", RuntimeConfiguration.Checked)] [Theory] [MemberData(nameof(TestCases))] public void InstanceMultiAppendGetCurrentHash(TestCase testCase) diff --git a/src/libraries/System.IO.Hashing/tests/XxHash64Tests.f00d.cs b/src/libraries/System.IO.Hashing/tests/XxHash64Tests.f00d.cs index 23006a571627b..79d370e93d197 100644 --- a/src/libraries/System.IO.Hashing/tests/XxHash64Tests.f00d.cs +++ b/src/libraries/System.IO.Hashing/tests/XxHash64Tests.f00d.cs @@ -115,6 +115,7 @@ public void InstanceAppendAllocateAndReset(TestCase testCase) InstanceAppendAllocateAndResetDriver(testCase); } + [SkipOnCoreClr("https://github.com/dotnet/runtime/issues/54007", RuntimeConfiguration.Checked)] [Theory] [MemberData(nameof(TestCases))] public void InstanceMultiAppendGetCurrentHash(TestCase testCase) From 24950a310bde0d816060f920090b4e9666c15fed Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Sat, 19 Jun 2021 13:31:12 -0700 Subject: [PATCH 027/107] Remove System.Security.Cryptography.Cng from ASP.NET transport package (#54428) --- .../System.Security.Cryptography.Cng/Directory.Build.props | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libraries/System.Security.Cryptography.Cng/Directory.Build.props b/src/libraries/System.Security.Cryptography.Cng/Directory.Build.props index 7bdd796606be8..709a22a753708 100644 --- a/src/libraries/System.Security.Cryptography.Cng/Directory.Build.props +++ b/src/libraries/System.Security.Cryptography.Cng/Directory.Build.props @@ -2,7 +2,6 @@ Microsoft - true windows \ No newline at end of file From 30f4269767c46317cfa5647d46eba4be837c1817 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksey=20Kliger=20=28=CE=BBgeek=29?= Date: Mon, 21 Jun 2021 03:13:44 -0400 Subject: [PATCH 028/107] Allow mono samples to build and run again (#54089) --- Directory.Build.props | 11 +++++++++++ src/libraries/Directory.Build.props | 9 --------- src/mono/sample/Android/AndroidSampleApp.csproj | 1 - src/mono/sample/iOS/Program.csproj | 1 - src/mono/sample/mbr/apple/AppleDelta.csproj | 1 - src/mono/wasm/build/WasmApp.InTree.props | 1 - src/mono/wasm/wasm.proj | 3 --- 7 files changed, 11 insertions(+), 16 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 7efd3a5729169..d1b03d7ef8de0 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -202,6 +202,17 @@ true + + $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'microsoft.netcore.app.ref')) + $([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRefPackDir)', 'ref', '$(NetCoreAppCurrent)')) + $([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRefPackDir)', 'data')) + + $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'microsoft.netcore.app.runtime.$(PackageRID)', '$(Configuration)')) + $([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRuntimePackDir)', 'runtimes', '$(PackageRID)')) + $([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRuntimePackRidDir)', 'lib', '$(NetCoreAppCurrent)')) + $([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRuntimePackRidDir)', 'native')) + + true diff --git a/src/libraries/Directory.Build.props b/src/libraries/Directory.Build.props index cc5a02e38691d..b06fc950644f2 100644 --- a/src/libraries/Directory.Build.props +++ b/src/libraries/Directory.Build.props @@ -136,15 +136,6 @@ $(ArtifactsBinDir)pkg\aspnetcoreapp\ref $(ArtifactsBinDir)pkg\aspnetcoreapp\lib - $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'microsoft.netcore.app.ref')) - $([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRefPackDir)', 'ref', '$(NetCoreAppCurrent)')) - $([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRefPackDir)', 'data')) - - $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'microsoft.netcore.app.runtime.$(PackageRID)', '$(Configuration)')) - $([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRuntimePackDir)', 'runtimes', '$(PackageRID)')) - $([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRuntimePackRidDir)', 'lib', '$(NetCoreAppCurrent)')) - $([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRuntimePackRidDir)', 'native')) - $([MSBuild]::NormalizeDirectory('$(LibrariesProjectRoot)', 'Common')) $([MSBuild]::NormalizeDirectory('$(CommonPathRoot)', 'src')) $([MSBuild]::NormalizeDirectory('$(CommonPathRoot)', 'tests')) diff --git a/src/mono/sample/Android/AndroidSampleApp.csproj b/src/mono/sample/Android/AndroidSampleApp.csproj index 4f5a245e314de..8fb25b90b7189 100644 --- a/src/mono/sample/Android/AndroidSampleApp.csproj +++ b/src/mono/sample/Android/AndroidSampleApp.csproj @@ -6,7 +6,6 @@ android-$(TargetArchitecture) true Link - $(ArtifactsBinDir)microsoft.netcore.app.runtime.$(RuntimeIdentifier)\$(Configuration)\ false diff --git a/src/mono/sample/iOS/Program.csproj b/src/mono/sample/iOS/Program.csproj index e9f7c50320597..04b2bde9b0b69 100644 --- a/src/mono/sample/iOS/Program.csproj +++ b/src/mono/sample/iOS/Program.csproj @@ -5,7 +5,6 @@ $(NetCoreAppToolCurrent) iOS iOSSimulator - $(ArtifactsBinDir)microsoft.netcore.app.runtime.$(TargetOS.ToLower())-$(TargetArchitecture)\$(Configuration)\ $(TargetOS.ToLower())-$(TargetArchitecture) $(DefineConstants);CI_TEST diff --git a/src/mono/sample/mbr/apple/AppleDelta.csproj b/src/mono/sample/mbr/apple/AppleDelta.csproj index 090fe47dc7afa..af4ec2b324a10 100644 --- a/src/mono/sample/mbr/apple/AppleDelta.csproj +++ b/src/mono/sample/mbr/apple/AppleDelta.csproj @@ -3,7 +3,6 @@ Exe bin $(NetCoreAppToolCurrent) - $(ArtifactsBinDir)microsoft.netcore.app.runtime.$(TargetOS.ToLower())-$(TargetArchitecture)\$(Configuration)\ $(TargetOS.ToLower())-$(TargetArchitecture) $(DefineConstants);CI_TEST true diff --git a/src/mono/wasm/build/WasmApp.InTree.props b/src/mono/wasm/build/WasmApp.InTree.props index 42b7574251468..e5968f1320a94 100644 --- a/src/mono/wasm/build/WasmApp.InTree.props +++ b/src/mono/wasm/build/WasmApp.InTree.props @@ -6,7 +6,6 @@ AnyCPU false $(NetCoreAppToolCurrent) - $([MSBuild]::NormalizeDirectory($(ArtifactsBinDir), 'microsoft.netcore.app.runtime.browser-wasm', $(Configuration), 'runtimes', 'browser-wasm')) $([MSBuild]::NormalizeDirectory($(MonoProjectRoot), 'wasm', 'emsdk')) false true diff --git a/src/mono/wasm/wasm.proj b/src/mono/wasm/wasm.proj index 81ce7272efebc..489d88259f5f3 100644 --- a/src/mono/wasm/wasm.proj +++ b/src/mono/wasm/wasm.proj @@ -7,9 +7,6 @@ browser-wasm $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'native', '$(NetCoreAppCurrent)-$(TargetOS)-$(Configuration)-$(TargetArchitecture)')) $([MSBuild]::NormalizeDirectory('$(PkgMicrosoft_NETCore_Runtime_ICU_Transport)', 'runtimes', 'browser-wasm', 'native', 'lib')) - $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'microsoft.netcore.app.runtime.$(PackageRID)', '$(Configuration)')) - $([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRuntimePackDir)', 'runtimes', '$(PackageRID)')) - $([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRuntimePackRidDir)', 'native')) false false emcc From f891033db5b8ebf651176a3dcc3bec74a217f85e Mon Sep 17 00:00:00 2001 From: Maxim Lipnin Date: Mon, 21 Jun 2021 10:20:03 +0300 Subject: [PATCH 029/107] Remove redundant P/Invoke-s in S.D.Process on Apple platforms (#54273) * Remove P/Invoke to SystemNative_ConfigureTerminalForChildProcess which doesn't exist on Apple platforms * Address the feedback based on the ifdef approach * Add IsiOSLike property * Use a partial method approach * Address the feedback --- .../src/System.Diagnostics.Process.csproj | 10 ++++-- ...Unix.ConfigureTerminalForChildProcesses.cs | 36 +++++++++++++++++++ .../src/System/Diagnostics/Process.Unix.cs | 24 ++----------- 3 files changed, 47 insertions(+), 23 deletions(-) create mode 100644 src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.ConfigureTerminalForChildProcesses.cs diff --git a/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj b/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj index f0c7d8eb136bf..cdd65c22c4431 100644 --- a/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj +++ b/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj @@ -10,6 +10,9 @@ SR.Process_PlatformNotSupported + + true + @@ -234,8 +237,6 @@ Link="Common\Interop\Unix\Interop.GetHostName.cs" /> - + + + + diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.ConfigureTerminalForChildProcesses.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.ConfigureTerminalForChildProcesses.cs new file mode 100644 index 0000000000000..6f84319b0c096 --- /dev/null +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.ConfigureTerminalForChildProcesses.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Threading; + +namespace System.Diagnostics +{ + public partial class Process + { + private static int s_childrenUsingTerminalCount; + + static partial void ConfigureTerminalForChildProcessesInner(int increment) + { + Debug.Assert(increment != 0); + + int childrenUsingTerminalRemaining = Interlocked.Add(ref s_childrenUsingTerminalCount, increment); + if (increment > 0) + { + Debug.Assert(s_processStartLock.IsReadLockHeld); + + // At least one child is using the terminal. + Interop.Sys.ConfigureTerminalForChildProcess(childUsesTerminal: true); + } + else + { + Debug.Assert(s_processStartLock.IsWriteLockHeld); + + if (childrenUsingTerminalRemaining == 0) + { + // No more children are using the terminal. + Interop.Sys.ConfigureTerminalForChildProcess(childUsesTerminal: false); + } + } + } + } +} diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs index 0894dd9e6f7a0..1b52e4d141ebb 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs @@ -19,7 +19,6 @@ public partial class Process : IDisposable private static volatile bool s_initialized; private static readonly object s_initializedGate = new object(); private static readonly ReaderWriterLockSlim s_processStartLock = new ReaderWriterLockSlim(); - private static int s_childrenUsingTerminalCount; /// /// Puts a Process component in state to interact with operating system processes that run in a @@ -1051,26 +1050,9 @@ private static void OnSigChild(int reapAll) /// internal static void ConfigureTerminalForChildProcesses(int increment) { - Debug.Assert(increment != 0); - - int childrenUsingTerminalRemaining = Interlocked.Add(ref s_childrenUsingTerminalCount, increment); - if (increment > 0) - { - Debug.Assert(s_processStartLock.IsReadLockHeld); - - // At least one child is using the terminal. - Interop.Sys.ConfigureTerminalForChildProcess(childUsesTerminal: true); - } - else - { - Debug.Assert(s_processStartLock.IsWriteLockHeld); - - if (childrenUsingTerminalRemaining == 0) - { - // No more children are using the terminal. - Interop.Sys.ConfigureTerminalForChildProcess(childUsesTerminal: false); - } - } + ConfigureTerminalForChildProcessesInner(increment); } + + static partial void ConfigureTerminalForChildProcessesInner(int increment); } } From a3f0e2bebe30fd5e82518d86cefc7895127ae474 Mon Sep 17 00:00:00 2001 From: Daniel Genkin Date: Mon, 21 Jun 2021 10:10:58 -0400 Subject: [PATCH 030/107] [WASM] Converted mono-config.js to mono-config.json (#53606) * converted mono-config.js to mono-config.json * Fixed test * fixed handling of case where Module isn't defined * Fixed remaining failing tests * Replaced alerts with console.log to fix Helix test * replaced all vars with consts * use fetch * clean syntax * prevent timeouts when the mono-config file fails to load * Moved config file loading to preInit * Fixed tests * Adjusted file linking * removed the unnecessary js_support.js * cleaned up function * updated samples * removed lingering pre-js flag * Fixed trimming tests * addressed PR comments * removed useless function --- src/mono/sample/mbr/browser/index.html | 1 - src/mono/sample/mbr/browser/runtime.js | 20 ++++- src/mono/sample/wasm/browser-bench/index.html | 1 - src/mono/sample/wasm/browser-bench/runtime.js | 27 +++++- .../sample/wasm/browser-profile/index.html | 59 ------------- .../sample/wasm/browser-profile/runtime.js | 86 ++++++++++++++++--- src/mono/sample/wasm/browser/index.html | 1 - src/mono/sample/wasm/browser/runtime.js | 19 +++- src/mono/wasm/build/WasmApp.targets | 2 +- .../tests/debugger-test/debugger-driver.html | 1 - .../tests/debugger-test/runtime-debugger.js | 18 +++- src/mono/wasm/runtime-test.js | 17 ++-- src/mono/wasm/runtime/library_mono.js | 27 +++++- src/tasks/WasmAppBuilder/WasmAppBuilder.cs | 6 +- .../Wasm.Build.Tests/BuildTestBase.cs | 2 +- .../WebAssembly/Browser/AOT/index.html | 1 - .../WebAssembly/Browser/AOT/runtime.js | 19 +++- .../Browser/NormalInterp/index.html | 1 - .../Browser/NormalInterp/runtime.js | 19 +++- 19 files changed, 218 insertions(+), 109 deletions(-) diff --git a/src/mono/sample/mbr/browser/index.html b/src/mono/sample/mbr/browser/index.html index 472cf5f29ea05..eb19b7bb2f595 100644 --- a/src/mono/sample/mbr/browser/index.html +++ b/src/mono/sample/mbr/browser/index.html @@ -29,7 +29,6 @@ }, }; - diff --git a/src/mono/sample/mbr/browser/runtime.js b/src/mono/sample/mbr/browser/runtime.js index 0856b8d0e0303..32918aa573a44 100644 --- a/src/mono/sample/mbr/browser/runtime.js +++ b/src/mono/sample/mbr/browser/runtime.js @@ -2,17 +2,29 @@ // The .NET Foundation licenses this file to you under the MIT license. var Module = { + config: null, + + preInit: async function() { + Module.config = await MONO.mono_wasm_load_config("./mono-config.json"); + }, + + // Called when the runtime is initialized and wasm is ready onRuntimeInitialized: function () { - config.loaded_cb = function () { + if (!Module.config || Module.config.error) { + console.log("An error occured while loading the config file"); + return; + } + + Module.config.loaded_cb = function () { App.init (); }; - config.environment_variables = { + Module.config.environment_variables = { "DOTNET_MODIFIABLE_ASSEMBLIES": "debug" }; - config.fetch_file_cb = function (asset) { + Module.config.fetch_file_cb = function (asset) { return fetch (asset, { credentials: 'same-origin' }); } - MONO.mono_load_runtime_and_bcl_args (config); + MONO.mono_load_runtime_and_bcl_args (Module.config); }, }; diff --git a/src/mono/sample/wasm/browser-bench/index.html b/src/mono/sample/wasm/browser-bench/index.html index 8f7748798d45b..5c5271954f1c2 100644 --- a/src/mono/sample/wasm/browser-bench/index.html +++ b/src/mono/sample/wasm/browser-bench/index.html @@ -51,7 +51,6 @@ } }; - diff --git a/src/mono/sample/wasm/browser-bench/runtime.js b/src/mono/sample/wasm/browser-bench/runtime.js index a39b0b97b1490..60c383d13cb52 100644 --- a/src/mono/sample/wasm/browser-bench/runtime.js +++ b/src/mono/sample/wasm/browser-bench/runtime.js @@ -1,9 +1,20 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. - var Module = { + config: null, + + preInit: async function() { + Module.config = await MONO.mono_wasm_load_config("./mono-config.json"); + }, + + // Called when the runtime is initialized and wasm is ready onRuntimeInitialized: function () { - config.loaded_cb = function () { + if (!Module.config || Module.config.error) { + console.log("An error occured while loading the config file"); + return; + } + + Module.config.loaded_cb = function () { try { App.init (); } catch (error) { @@ -11,13 +22,21 @@ var Module = { throw (error); } }; - config.fetch_file_cb = function (asset) { + Module.config.fetch_file_cb = function (asset) { return fetch (asset, { credentials: 'same-origin' }); } + if (Module.config.enable_profiler) + { + Module.config.aot_profiler_options = { + write_at:"Sample.Test::StopProfile", + send_to: "System.Runtime.InteropServices.JavaScript.Runtime::DumpAotProfileData" + } + } + try { - MONO.mono_load_runtime_and_bcl_args (config); + MONO.mono_load_runtime_and_bcl_args (Module.config); } catch (error) { test_exit(1); throw(error); diff --git a/src/mono/sample/wasm/browser-profile/index.html b/src/mono/sample/wasm/browser-profile/index.html index 50eb0ff28c2ef..de4a5599e94ad 100644 --- a/src/mono/sample/wasm/browser-profile/index.html +++ b/src/mono/sample/wasm/browser-profile/index.html @@ -10,67 +10,8 @@ Result from Sample.Test.TestMeaning: - - - diff --git a/src/mono/sample/wasm/browser-profile/runtime.js b/src/mono/sample/wasm/browser-profile/runtime.js index 1ce1c0b736034..2c83ff54ef93d 100644 --- a/src/mono/sample/wasm/browser-profile/runtime.js +++ b/src/mono/sample/wasm/browser-profile/runtime.js @@ -1,22 +1,35 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -var Module = { +var Module = { + is_testing: false, + config: null, + + preInit: async function() { + Module.config = await MONO.mono_wasm_load_config("./mono-config.json"); + }, + + // Called when the runtime is initialized and wasm is ready onRuntimeInitialized: function () { - config.loaded_cb = function () { + if (!Module.config || Module.config.error) { + console.log("An error occured while loading the config file"); + return; + } + + Module.config.loaded_cb = function () { try { - App.init (); + Module.init(); } catch (error) { - test_exit(1); + Module.test_exit(1); throw (error); } }; - config.fetch_file_cb = function (asset) { + Module.config.fetch_file_cb = function (asset) { return fetch (asset, { credentials: 'same-origin' }); } - if (config.enable_profiler) + if (Module.config.enable_profiler) { - config.aot_profiler_options = { + Module.config.aot_profiler_options = { write_at:"Sample.Test::StopProfile", send_to: "System.Runtime.InteropServices.JavaScript.Runtime::DumpAotProfileData" } @@ -24,10 +37,63 @@ var Module = { try { - MONO.mono_load_runtime_and_bcl_args (config); + MONO.mono_load_runtime_and_bcl_args (Module.config); } catch (error) { - test_exit(1); + Module.test_exit(1); throw(error); } + }, + + init: function () { + console.log("not ready yet") + var ret = BINDING.call_static_method("[Wasm.BrowserProfile.Sample] Sample.Test:TestMeaning", []); + document.getElementById("out").innerHTML = ret; + console.log ("ready"); + + if (Module.is_testing) + { + console.debug(`ret: ${ret}`); + let exit_code = ret == 42 ? 0 : 1; + Module.test_exit(exit_code); + } + + if (Module.config.enable_profiler) { + BINDING.call_static_method("[Wasm.BrowserProfile.Sample] Sample.Test:StopProfile", []); + Module.saveProfile(); + } + }, + + onLoad: function() { + var url = new URL(decodeURI(window.location)); + let args = url.searchParams.getAll('arg'); + Module.is_testing = args !== undefined && (args.find(arg => arg == '--testing') !== undefined); + }, + + test_exit: function(exit_code) { + if (!Module.is_testing) { + console.log(`test_exit: ${exit_code}`); + return; + } + + /* Set result in a tests_done element, to be read by xharness */ + var tests_done_elem = document.createElement("label"); + tests_done_elem.id = "tests_done"; + tests_done_elem.innerHTML = exit_code.toString(); + document.body.appendChild(tests_done_elem); + + console.log(`WASM EXIT ${exit_code}`); + }, + + saveProfile: function () { + var a = document.createElement('a'); + var blob = new Blob([Module.aot_profile_data]); + a.href = URL.createObjectURL(blob); + a.download = "data.aotprofile"; + // Append anchor to body. + document.body.appendChild(a); + a.click(); + + // Remove anchor from body + document.body.removeChild(a); } -}; \ No newline at end of file +}; diff --git a/src/mono/sample/wasm/browser/index.html b/src/mono/sample/wasm/browser/index.html index bd8e5015a0ee5..5f170bb38e7bc 100644 --- a/src/mono/sample/wasm/browser/index.html +++ b/src/mono/sample/wasm/browser/index.html @@ -48,7 +48,6 @@ }, }; - diff --git a/src/mono/sample/wasm/browser/runtime.js b/src/mono/sample/wasm/browser/runtime.js index a39b0b97b1490..e97feef745a95 100644 --- a/src/mono/sample/wasm/browser/runtime.js +++ b/src/mono/sample/wasm/browser/runtime.js @@ -2,8 +2,21 @@ // The .NET Foundation licenses this file to you under the MIT license. var Module = { + + config: null, + + preInit: async function() { + Module.config = await MONO.mono_wasm_load_config("./mono-config.json"); + }, + + // Called when the runtime is initialized and wasm is ready onRuntimeInitialized: function () { - config.loaded_cb = function () { + if (!Module.config || Module.config.error) { + console.log("No config found"); + return; + } + + Module.config.loaded_cb = function () { try { App.init (); } catch (error) { @@ -11,13 +24,13 @@ var Module = { throw (error); } }; - config.fetch_file_cb = function (asset) { + Module.config.fetch_file_cb = function (asset) { return fetch (asset, { credentials: 'same-origin' }); } try { - MONO.mono_load_runtime_and_bcl_args (config); + MONO.mono_load_runtime_and_bcl_args (Module.config); } catch (error) { test_exit(1); throw(error); diff --git a/src/mono/wasm/build/WasmApp.targets b/src/mono/wasm/build/WasmApp.targets index 2141cce41b69b..b886f327d7162 100644 --- a/src/mono/wasm/build/WasmApp.targets +++ b/src/mono/wasm/build/WasmApp.targets @@ -65,7 +65,7 @@ - @(WasmFilesToIncludeInFileSystem) - Files to include in the vfs - @(WasmNativeAsset) - Native files to be added to `NativeAssets` in the bundle. - - @(WasmExtraConfig) - json elements to add to `mono-config.js` + - @(WasmExtraConfig) - json elements to add to `mono-config.json` Eg. - Value attribute can have a number, bool, quoted string, or json string diff --git a/src/mono/wasm/debugger/tests/debugger-test/debugger-driver.html b/src/mono/wasm/debugger/tests/debugger-test/debugger-driver.html index 87ba804792ef3..b1bfcd859d064 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/debugger-driver.html +++ b/src/mono/wasm/debugger/tests/debugger-test/debugger-driver.html @@ -83,7 +83,6 @@ return App.int_add (a, b); } - diff --git a/src/mono/wasm/debugger/tests/debugger-test/runtime-debugger.js b/src/mono/wasm/debugger/tests/debugger-test/runtime-debugger.js index 4c641eb2c6a0d..360aa9b8b3036 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/runtime-debugger.js +++ b/src/mono/wasm/debugger/tests/debugger-test/runtime-debugger.js @@ -2,8 +2,20 @@ // The .NET Foundation licenses this file to you under the MIT license. var Module = { - onRuntimeInitialized: function () { - config.loaded_cb = function () { + config: null, + + preInit: async function() { + Module.config = await MONO.mono_wasm_load_config("./mono-config.json"); + }, + + // Called when the runtime is initialized and wasm is ready + onRuntimeInitialized: function () { + if (!Module.config || Module.config.error) { + console.log("An error occured while loading the config file"); + return; + } + + Module.config.loaded_cb = function () { App.init (); }; // For custom logging patch the functions below @@ -15,6 +27,6 @@ var Module = { MONO.mono_wasm_setenv ("MONO_LOG_LEVEL", "debug"); MONO.mono_wasm_setenv ("MONO_LOG_MASK", "all"); */ - MONO.mono_load_runtime_and_bcl_args (config) + MONO.mono_load_runtime_and_bcl_args (Module.config) }, }; diff --git a/src/mono/wasm/runtime-test.js b/src/mono/wasm/runtime-test.js index 74aa1083c033d..8b3395f4ec50e 100644 --- a/src/mono/wasm/runtime-test.js +++ b/src/mono/wasm/runtime-test.js @@ -204,14 +204,16 @@ function loadScript (url) } } -loadScript ("mono-config.js"); - var Module = { mainScriptUrlOrBlob: "dotnet.js", - + config: null, print, printErr, + preInit: async function() { + Module.config = await MONO.mono_wasm_load_config("./mono-config.json"); + }, + onAbort: function(x) { print ("ABORT: " + x); var err = new Error(); @@ -230,7 +232,7 @@ var Module = { Module.ccall ('mono_wasm_enable_on_demand_gc', 'void', ['number'], [0]); } - config.loaded_cb = function () { + Module.config.loaded_cb = function () { let wds = FS.stat (working_dir); if (wds === undefined || !FS.isDir (wds.mode)) { fail_exec (`Could not find working directory ${working_dir}`); @@ -240,13 +242,13 @@ var Module = { FS.chdir (working_dir); App.init (); }; - config.fetch_file_cb = function (asset) { + Module.config.fetch_file_cb = function (asset) { // console.log("fetch_file_cb('" + asset + "')"); // for testing purposes add BCL assets to VFS until we special case File.Open // to identify when an assembly from the BCL is being open and resolve it correctly. /* var content = new Uint8Array (read (asset, 'binary')); - var path = asset.substr(config.deploy_prefix.length); + var path = asset.substr(Module.config.deploy_prefix.length); writeContentToFile(content, path); */ @@ -280,10 +282,9 @@ var Module = { } }; - MONO.mono_load_runtime_and_bcl_args (config); + MONO.mono_load_runtime_and_bcl_args (Module.config); }, }; - loadScript ("dotnet.js"); const IGNORE_PARAM_COUNT = -1; diff --git a/src/mono/wasm/runtime/library_mono.js b/src/mono/wasm/runtime/library_mono.js index 11866606f4dce..36837f62cf709 100644 --- a/src/mono/wasm/runtime/library_mono.js +++ b/src/mono/wasm/runtime/library_mono.js @@ -86,6 +86,7 @@ var MonoSupportLib = { module ["mono_wasm_new_root"] = MONO.mono_wasm_new_root.bind(MONO); module ["mono_wasm_new_roots"] = MONO.mono_wasm_new_roots.bind(MONO); module ["mono_wasm_release_roots"] = MONO.mono_wasm_release_roots.bind(MONO); + module ["mono_wasm_load_config"] = MONO.mono_wasm_load_config.bind(MONO); }, _base64Converter: { @@ -2362,6 +2363,30 @@ var MonoSupportLib = { console.debug('mono_wasm_debug_event_raised:aef14bca-5519-4dfe-b35a-f867abc123ae', JSON.stringify(event), JSON.stringify(args)); }, + + /** + * Loads the mono config file (typically called mono-config.json) + * + * @param {string} configFilePath - relative path to the config file + * @throws Will throw an error if the config file loading fails + */ + mono_wasm_load_config: async function (configFilePath) { + try { + let config = null; + // NOTE: when we add nodejs make sure to include the nodejs fetch package + if (ENVIRONMENT_IS_WEB) { + const configRaw = await fetch(configFilePath); + config = await configRaw.json(); + }else if (ENVIRONMENT_IS_NODE) { + config = require(configFilePath); + } else { // shell or worker + config = JSON.parse(read(configFilePath)); // read is a v8 debugger command + } + return config; + } catch(e) { + return {message: "failed to load config file", error: e}; + } + } }, mono_wasm_add_typed_value: function (type, str_value, value) { @@ -2549,7 +2574,7 @@ var MonoSupportLib = { assembly_b64, pdb_b64 }); - }, + } }; autoAddDeps(MonoSupportLib, '$MONO') diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs index 9ef7679c292b6..49d984490a64b 100644 --- a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs +++ b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs @@ -51,7 +51,7 @@ public class WasmAppBuilder : Task public ITaskItem[]? ExtraFilesToDeploy { get; set; } // - // Extra json elements to add to mono-config.js + // Extra json elements to add to mono-config.json // // Metadata: // - Value: can be a number, bool, quoted string, or json string @@ -246,11 +246,11 @@ public override bool Execute () config.Extra[name] = valueObject; } - string monoConfigPath = Path.Combine(AppDir, "mono-config.js"); + string monoConfigPath = Path.Combine(AppDir, "mono-config.json"); using (var sw = File.CreateText(monoConfigPath)) { var json = JsonSerializer.Serialize (config, new JsonSerializerOptions { WriteIndented = true }); - sw.Write($"config = {json};"); + sw.Write(json); } _fileWrites.Add(monoConfigPath); diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs index b5f1f7e0b95f7..99e721dfa65f7 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs @@ -347,7 +347,7 @@ protected static void AssertBasicAppBundle(string bundleDir, string projectName, "runtime.js", "dotnet.timezones.blat", "dotnet.wasm", - "mono-config.js", + "mono-config.json", "dotnet.js", "run-v8.sh" }); diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/AOT/index.html b/src/tests/FunctionalTests/WebAssembly/Browser/AOT/index.html index efab9ac4324a2..642987d23c5e2 100644 --- a/src/tests/FunctionalTests/WebAssembly/Browser/AOT/index.html +++ b/src/tests/FunctionalTests/WebAssembly/Browser/AOT/index.html @@ -47,7 +47,6 @@ }, }; - diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/AOT/runtime.js b/src/tests/FunctionalTests/WebAssembly/Browser/AOT/runtime.js index a39b0b97b1490..65cba13a9b126 100644 --- a/src/tests/FunctionalTests/WebAssembly/Browser/AOT/runtime.js +++ b/src/tests/FunctionalTests/WebAssembly/Browser/AOT/runtime.js @@ -2,8 +2,21 @@ // The .NET Foundation licenses this file to you under the MIT license. var Module = { + + config: null, + + preInit: async function() { + Module.config = await MONO.mono_wasm_load_config("./mono-config.json"); + }, + onRuntimeInitialized: function () { - config.loaded_cb = function () { + if (!Module.config || Module.config.error) { + console.log("No config found"); + test_exit(1); + throw(Module.config.error); + } + + Module.config.loaded_cb = function () { try { App.init (); } catch (error) { @@ -11,13 +24,13 @@ var Module = { throw (error); } }; - config.fetch_file_cb = function (asset) { + Module.config.fetch_file_cb = function (asset) { return fetch (asset, { credentials: 'same-origin' }); } try { - MONO.mono_load_runtime_and_bcl_args (config); + MONO.mono_load_runtime_and_bcl_args (Module.config); } catch (error) { test_exit(1); throw(error); diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/index.html b/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/index.html index 03f68679a5b85..9de05f5031b32 100644 --- a/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/index.html +++ b/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/index.html @@ -47,7 +47,6 @@ }, }; - diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/runtime.js b/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/runtime.js index a39b0b97b1490..1a8abf503fb33 100644 --- a/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/runtime.js +++ b/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/runtime.js @@ -2,8 +2,21 @@ // The .NET Foundation licenses this file to you under the MIT license. var Module = { + + config: null, + + preInit: async function() { + Module.config = await MONO.mono_wasm_load_config("./mono-config.json"); + }, + onRuntimeInitialized: function () { - config.loaded_cb = function () { + if (!Module.config || Module.config.error) { + console.log("No config found"); + test_exit(1); + throw(Module.config.error); + } + + Module.config.loaded_cb = function () { try { App.init (); } catch (error) { @@ -11,13 +24,13 @@ var Module = { throw (error); } }; - config.fetch_file_cb = function (asset) { + Module.config.fetch_file_cb = function (asset) { return fetch (asset, { credentials: 'same-origin' }); } try { - MONO.mono_load_runtime_and_bcl_args (config); + MONO.mono_load_runtime_and_bcl_args (Module.config); } catch (error) { test_exit(1); throw(error); From 1cf9e342e744b96423d59f11c8e203788a5cf52b Mon Sep 17 00:00:00 2001 From: xtqqczze <45661989+xtqqczze@users.noreply.github.com> Date: Mon, 21 Jun 2021 16:21:52 +0100 Subject: [PATCH 031/107] Clarify Visual Studio minimum requirement (#52820) --- docs/workflow/requirements/windows-requirements.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/workflow/requirements/windows-requirements.md b/docs/workflow/requirements/windows-requirements.md index 56f662f6f2199..2629df320d288 100644 --- a/docs/workflow/requirements/windows-requirements.md +++ b/docs/workflow/requirements/windows-requirements.md @@ -34,7 +34,7 @@ Visual Studio 2019 installation process: A `.vsconfig` file is included in the root of the dotnet/runtime repository that includes all components needed to build the dotnet/runtime repository. You can [import `.vsconfig` in your Visual Studio installer](https://docs.microsoft.com/en-us/visualstudio/install/import-export-installation-configurations?view=vs-2019#import-a-configuration) to install all necessary components. -The dotnet/runtime repository requires at least Visual Studio 2019 16.6. +Visual Studio 2019 16.6 or later is required for building the repository. Visual Studio 2019 16.10 is required to work with the libraries projects inside the Visual Studio IDE. ## CMake From 0049c629381c5a18e4dadd1038c2bd6b3ae6e3e6 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Mon, 21 Jun 2021 08:24:16 -0700 Subject: [PATCH 032/107] Fix the slot calculation for multiple services (#54462) --- .../src/ServiceLookup/CallSiteFactory.cs | 13 ++- .../ServiceLookup/CallSiteFactoryTest.cs | 35 ++++++++ .../DI.Tests/ServiceProviderContainerTests.cs | 85 ++++++++++++++++--- 3 files changed, 122 insertions(+), 11 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteFactory.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteFactory.cs index da791135ff082..a2e02ce4f5001 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteFactory.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteFactory.cs @@ -77,6 +77,17 @@ private void Populate() } } + // For unit testing + internal int? GetSlot(ServiceDescriptor serviceDescriptor) + { + if (_descriptorLookup.TryGetValue(serviceDescriptor.ServiceType, out ServiceDescriptorCacheItem item)) + { + return item.GetSlot(serviceDescriptor); + } + + return null; + } + internal ServiceCallSite GetCallSite(Type serviceType, CallSiteChain callSiteChain) => _callSiteCache.TryGetValue(new ServiceCacheKey(serviceType, DefaultSlot), out ServiceCallSite site) ? site : CreateCallSite(serviceType, callSiteChain); @@ -537,7 +548,7 @@ public int GetSlot(ServiceDescriptor descriptor) int index = _items.IndexOf(descriptor); if (index != -1) { - return Count - index + 1; + return _items.Count - (index + 1); } } diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceLookup/CallSiteFactoryTest.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceLookup/CallSiteFactoryTest.cs index e027508f784f8..8d6a78029feff 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceLookup/CallSiteFactoryTest.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceLookup/CallSiteFactoryTest.cs @@ -556,6 +556,41 @@ public void CreateCallSite_ThrowsIfTypeHasSingleConstructorWithUnresolvableParam ex.Message); } + [Theory] + [InlineData(1)] + [InlineData(2)] + [InlineData(3)] + [InlineData(4)] + [InlineData(5)] + public void GetSlotTests(int numberOfServices) + { + var serviceDescriptors = new[] { + ServiceDescriptor.Singleton(), + ServiceDescriptor.Singleton(), + ServiceDescriptor.Singleton(), + ServiceDescriptor.Singleton(), + ServiceDescriptor.Singleton() + }; + + var callsiteFactory = new CallSiteFactory(serviceDescriptors.Take(numberOfServices)); + + for (int i = 0; i < numberOfServices; i++) + { + Assert.Equal(numberOfServices - i - 1, callsiteFactory.GetSlot(serviceDescriptors[i])); + } + } + + interface ICustomService + { + + } + + class CustomService1 : ICustomService { } + class CustomService2 : ICustomService { } + class CustomService3 : ICustomService { } + class CustomService4 : ICustomService { } + class CustomService5 : ICustomService { } + [Theory] [InlineData(typeof(TypeWithMultipleParameterizedConstructors))] [InlineData(typeof(TypeWithSupersetConstructors))] diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderContainerTests.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderContainerTests.cs index 22fd778450fc0..6da83cf2d3ea7 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderContainerTests.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderContainerTests.cs @@ -7,6 +7,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Fakes; using Microsoft.Extensions.DependencyInjection.Specification; using Microsoft.Extensions.DependencyInjection.Specification.Fakes; @@ -80,6 +81,70 @@ public void AttemptingToIEnumerableResolveNonexistentServiceIndirectlyThrows() $"'{typeof(DependOnNonexistentService)}'.", ex.Message); } + [Theory] + [InlineData(1)] + [InlineData(2)] + [InlineData(3)] + [InlineData(4)] + [InlineData(5)] + [InlineData(6)] + public void MultipleServicesAreOrdered(int numberOfServices) + { + // Arrange + var collection = new ServiceCollection(); + + var serviceDescriptors = new[] { + ServiceDescriptor.Singleton(), + ServiceDescriptor.Singleton(), + ServiceDescriptor.Singleton(), + ServiceDescriptor.Singleton(), + ServiceDescriptor.Singleton(), + ServiceDescriptor.Singleton() + }; + + var serviceTypes = new[] + { + typeof(CustomService1), + typeof(CustomService2), + typeof(CustomService3), + typeof(CustomService4), + typeof(CustomService5), + typeof(CustomService6), + }; + + foreach (var sd in serviceDescriptors.Take(numberOfServices)) + { + collection.Add(sd); + } + + var provider = collection.BuildServiceProvider(new ServiceProviderOptions + { + ValidateOnBuild = true + }); + + // Act and Assert + var customServices = provider.GetService>().ToArray(); + + Assert.Equal(numberOfServices, customServices.Length); + + for (int i = 0; i < numberOfServices; i++) + { + Assert.IsAssignableFrom(serviceTypes[i], customServices[i]); + } + } + + interface ICustomService + { + + } + + class CustomService1 : ICustomService { } + class CustomService2 : ICustomService { } + class CustomService3 : ICustomService { } + class CustomService4 : ICustomService { } + class CustomService5 : ICustomService { } + class CustomService6 : ICustomService { } + [Theory] // GenericTypeDefintion, Abstract GenericTypeDefintion [InlineData(typeof(IFakeOpenGenericService<>), typeof(AbstractFakeOpenGenericService<>))] @@ -121,11 +186,11 @@ public static IEnumerable FailedOpenGenericTypeTestData { get { - Type serviceType = typeof(IFakeOpenGenericService<>); - // Service type is GenericTypeDefintion, implementation type is ConstructedGenericType - yield return new object[] {serviceType, typeof(ClassWithNoConstraints), $"Open generic service type '{serviceType}' requires registering an open generic implementation type."}; - // Service type is GenericTypeDefintion, implementation type has different generic type definition arity - yield return new object[] {serviceType, typeof(FakeOpenGenericServiceWithTwoTypeArguments<,>), $"Arity of open generic service type '{serviceType}' does not equal arity of open generic implementation type '{typeof(FakeOpenGenericServiceWithTwoTypeArguments<,>)}'."}; + Type serviceType = typeof(IFakeOpenGenericService<>); + // Service type is GenericTypeDefintion, implementation type is ConstructedGenericType + yield return new object[] { serviceType, typeof(ClassWithNoConstraints), $"Open generic service type '{serviceType}' requires registering an open generic implementation type." }; + // Service type is GenericTypeDefintion, implementation type has different generic type definition arity + yield return new object[] { serviceType, typeof(FakeOpenGenericServiceWithTwoTypeArguments<,>), $"Arity of open generic service type '{serviceType}' does not equal arity of open generic implementation type '{typeof(FakeOpenGenericServiceWithTwoTypeArguments<,>)}'." }; } } @@ -566,8 +631,8 @@ public async Task GetRequiredService_UsesSingletonAndLazyLocks_NoDeadlock() sb.Append("4"); } - // Let Thread 1 over take Thread 2 - Thing1 value = lazy.Value; + // Let Thread 1 over take Thread 2 + Thing1 value = lazy.Value; return value; }); services.AddSingleton(); @@ -631,7 +696,7 @@ public async Task GetRequiredService_BiggerObjectGraphWithOpenGenerics_NoDeadloc sb.Append("3"); mreForThread2.Set(); // Now that thread 1 holds lazy lock, allow thread 2 to continue - thing3 = sp.GetRequiredService(); + thing3 = sp.GetRequiredService(); return new Thing4(thing3); }); @@ -1012,7 +1077,7 @@ public void Dispose() } } - private class FakeMultipleServiceWithIEnumerableDependency: IFakeMultipleService + private class FakeMultipleServiceWithIEnumerableDependency : IFakeMultipleService { public FakeMultipleServiceWithIEnumerableDependency(IEnumerable fakeServices) { @@ -1125,7 +1190,7 @@ private void ScopedServiceResolvedFromSingletonAfterCompilation2(ServiceProvider Assert.Same(sp.GetRequiredService>().Value, sp.GetRequiredService()); } - + [Theory] [InlineData(ServiceProviderMode.Default)] [InlineData(ServiceProviderMode.Dynamic)] From dbcb38752d3303705a463ea029c83b016e99816a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Takym=20=28=E3=81=9F=E3=81=8B=E3=82=84=E3=81=BE=29?= <15681312+Takym@users.noreply.github.com> Date: Tue, 22 Jun 2021 02:06:29 +0900 Subject: [PATCH 033/107] Syntax highlighted `type-loader.md` (#54497) * Syntax-highlighted `type-loader.md` * Update type-loader.md --- docs/design/coreclr/botr/type-loader.md | 88 +++++++++++++++---------- 1 file changed, 55 insertions(+), 33 deletions(-) diff --git a/docs/design/coreclr/botr/type-loader.md b/docs/design/coreclr/botr/type-loader.md index b873d8541a012..7c8b34627665b 100644 --- a/docs/design/coreclr/botr/type-loader.md +++ b/docs/design/coreclr/botr/type-loader.md @@ -76,21 +76,25 @@ There is a relatively small number of entry-points to the loader. Although the s There are usually many calls to the type loader during JITting. Consider: - object CreateClass() - { - return new MyClass(); - } +```csharp +object CreateClass() +{ + return new MyClass(); +} +``` In the IL, MyClass is referred to using a metadata token. In order to generate a call to the `JIT_New` helper which takes care of the actual instantiation, the JIT will ask the type loader to load the type and return a handle to it. This handle will be then directly embedded in the JITted code as an immediate value. The fact that types and members are usually resolved and loaded at JIT time and not at run-time also explains the sometimes confusing behavior easily hit with code like this: - object CreateClass() - { - try { - return new MyClass(); - } catch (TypeLoadException) { - return null; - } - } +```csharp +object CreateClass() +{ + try { + return new MyClass(); + } catch (TypeLoadException) { + return null; + } +} +``` If `MyClass` fails to load, for example because it's supposed to be defined in another assembly and it was accidentally removed in the newest build, then this code will still throw `TypeLoadException`. The reason that the catch block did not catch it is that it never ran! The exception occurred during JITting and would only be catchable in the method that called `CreateClass` and caused it to be JITted. In addition, it may not be always obvious at which point the JITting is triggered due to inlining, so users should not expect and rely on deterministic behavior. @@ -153,14 +157,16 @@ both the same type. When the type loader is asked to load a specified type, identified for example by a typedef/typeref/typespec **token** and a **Module** , it does not do all the work atomically at once. The loading is done in phases instead. The reason for this is that the type usually depends on other types and requiring it to be fully loaded before it can be referred to by other types would result in infinite recursion and deadlocks. Consider: - class A : C> - { } +```csharp +class A : C> +{ } - class B : C> - { } +class B : C> +{ } - class C - { } +class C +{ } +``` These are valid types and apparently `A` depends on `B` and `B` depends on `A`. @@ -195,10 +201,12 @@ A placeholder to be substituted by another type; the `T` in the declaration of ` A type being substituted for a generic parameter; the `int` in `List`. Note that a generic parameter can also be used as an argument. Consider: - List GetList() - { - return new List(); - } +```csharp +List GetList() +{ + return new List(); +} +``` The method has one generic parameter `T` which is used as a generic argument for the generic list class. @@ -209,28 +217,38 @@ An optional requirement placed by generic parameters on its potential generic ar 1. Special constraints - Reference type constraint - the generic argument must be a reference type (as opposed to a value type). The `class` keyword is used in C# to express this constraint. - public class A where T : class + ```csharp + public class A where T : class + ``` - Value type constraint - the generic argument must be a value type different from `System.Nullable`. C# uses the `struct` keyword. - public class A where T : struct + ```csharp + public class A where T : struct + ``` - Default constructor constraint - the generic argument must have a public parameterless constructor. This is expressed by `new()` in C#. - public class A where T : new() + ```csharp + public class A where T : new() + ``` 2. Base type constraints - the generic argument must be derived from (or directly be of) the given non-interface type. It obviously makes sense to use only zero or one reference type as a base types constraint. - public class A where T : EventArgs + ```csharp + public class A where T : EventArgs + ``` 3. Implemented interface constraints - the generic argument must implement (or directly be of) the given interface type. Zero or more interfaces can be given. - public class A where T : ICloneable, IComparable + ```csharp + public class A where T : ICloneable, IComparable + ``` The above constraints are combined with an implicit AND, i.e. a generic parameter can be constrained to be derived from a given type, @@ -239,11 +257,13 @@ generic parameters of the declaring type can be used to express the constraints, introducing interdependencies among the parameters. For example: - public class A - where S : T - where T : IList { - void f(V v) where V : S {} - } +```csharp +public class A + where S : T + where T : IList { + void f(V v) where V : S {} +} +``` **Instantiation** @@ -259,7 +279,9 @@ declared. There exists exactly one typical instantiation for each generic type and method. Usually when one talks about an open generic type, they have the typical instantiation in mind. Example: - public class A {} +```csharp +public class A {} +``` The C# `typeof(A<,,>)` compiles to ldtoken A\'3 which makes the runtime load ``A`3`` instantiated at `S` , `T` , `U`. From 9199eb6952a4569cf5be57b68732989651ca55e2 Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Mon, 21 Jun 2021 12:10:47 -0500 Subject: [PATCH 034/107] Remove BitmapSelector.Suffix property (#54364) With https://github.com/dotnet/runtime/issues/22761 and https://github.com/dotnet/corefx/pull/22833, BitmapSelector.Suffix will always be null. This means this feature is dead code, and users are unable to use it. Removing this dead code because: 1. It doesn't do anything. 2. It is causing ILLink warnings, and it is easier to delete than to try to address the warnings. I logged https://github.com/dotnet/runtime/issues/54363 to follow up and either re-implement this feature, or obsolete the attributes that no longer have any effect on the app. --- .../src/ILLink/ILLink.Suppressions.xml | 14 +- .../src/System/Drawing/BitmapSelector.cs | 209 +----------------- .../System/Drawing/ToolboxBitmapAttribute.cs | 19 -- 3 files changed, 7 insertions(+), 235 deletions(-) diff --git a/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml index ec106d4867087..d5734aab1a2a9 100644 --- a/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml @@ -1,18 +1,6 @@  - - ILLink - IL2026 - member - M:System.Drawing.BitmapSelector.SameAssemblyOptIn(System.Reflection.Assembly) - - - ILLink - IL2026 - member - M:System.Drawing.BitmapSelector.SatelliteAssemblyOptIn(System.Reflection.Assembly) - ILLink IL2050 @@ -80,4 +68,4 @@ M:System.Drawing.SafeNativeMethods.Gdip.GdipSaveImageToStream(System.Runtime.InteropServices.HandleRef,Interop.Ole32.IStream,System.Guid@,System.Runtime.InteropServices.HandleRef) - \ No newline at end of file + diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/BitmapSelector.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/BitmapSelector.cs index fcec880055ae2..b5daa033aa805 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/BitmapSelector.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/BitmapSelector.cs @@ -1,135 +1,16 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.IO; +using System.Reflection; + namespace System.Drawing { - using System.Diagnostics.CodeAnalysis; - using System.IO; - using System.Reflection; - /// - /// Provides methods to select from multiple bitmaps depending on a "bitmapSuffix" config setting. + /// Provides methods to select bitmaps. /// internal static class BitmapSelector { - /// - /// Gets the bitmap ID suffix defined in the application configuration, or string.Empty if - /// the suffix is not specified. Internal for unit tests - /// - /// - /// For performance, the suffix is cached in a static variable so it only has to be read - /// once per appdomain. - /// - private static string? s_suffix; - internal static string? Suffix - { - get - { - // NOTE: This value is read from the "SystemDrawingSection" of the ConfigurationManager on - // the .NET Framework. To avoid pulling in a direct dependency to that assembly, we are not - // reading the value in this implementation. - return s_suffix; - } - set - { - // So unit tests can clear the cached suffix - s_suffix = value; - } - } - - /// - /// Appends the current suffix to . The suffix is appended - /// before the existing extension (if any). Internal for unit tests. - /// - /// - /// The new path with the suffix included. If there is no suffix defined or there are - /// invalid characters in the original path, the original path is returned. - /// - internal static string AppendSuffix(string filePath) - { - try - { - return Path.ChangeExtension(filePath, Suffix + Path.GetExtension(filePath)); - } - catch (ArgumentException) - { // there are invalid characters in the path - return filePath; - } - } - - /// - /// Returns with the current suffix appended (before the - /// existing extension) if the resulting file path exists; otherwise the original path is - /// returned. - /// - public static string GetFileName(string originalPath) - { - if (string.IsNullOrEmpty(Suffix)) - return originalPath; - - string newPath = AppendSuffix(originalPath); - return File.Exists(newPath) ? newPath : originalPath; - } - - // Calls assembly.GetManifestResourceStream in a try/catch and returns null if not found - private static Stream? GetResourceStreamHelper(Assembly assembly, Type type, string name) - { - Stream? stream = null; - try - { - stream = assembly.GetManifestResourceStream(type, name); - } - catch (FileNotFoundException) - { - } - return stream; - } - - [RequiresUnreferencedCode("Calls Assembly.GetType which may be trimmed")] - private static bool DoesAssemblyHaveCustomAttribute(Assembly assembly, string typeName) - { - return DoesAssemblyHaveCustomAttribute(assembly, assembly.GetType(typeName)); - } - - private static bool DoesAssemblyHaveCustomAttribute(Assembly assembly, Type? attrType) - { - if (attrType != null) - { - var attr = assembly.GetCustomAttributes(attrType, false); - if (attr.Length > 0) - { - return true; - } - } - return false; - } - - // internal for unit tests - internal static bool SatelliteAssemblyOptIn(Assembly assembly) - { - // Try 4.5 public attribute type first - if (DoesAssemblyHaveCustomAttribute(assembly, typeof(BitmapSuffixInSatelliteAssemblyAttribute))) - { - return true; - } - - // Also load attribute type by name for dlls compiled against older frameworks - return DoesAssemblyHaveCustomAttribute(assembly, "System.Drawing.BitmapSuffixInSatelliteAssemblyAttribute"); - } - - // internal for unit tests - internal static bool SameAssemblyOptIn(Assembly assembly) - { - // Try 4.5 public attribute type first - if (DoesAssemblyHaveCustomAttribute(assembly, typeof(BitmapSuffixInSameAssemblyAttribute))) - { - return true; - } - - // Also load attribute type by name for dlls compiled against older frameworks - return DoesAssemblyHaveCustomAttribute(assembly, "System.Drawing.BitmapSuffixInSameAssemblyAttribute"); - } - /// /// Returns a resource stream loaded from the appropriate location according to the current /// suffix. @@ -138,57 +19,10 @@ internal static bool SameAssemblyOptIn(Assembly assembly) /// The type whose namespace is used to scope the manifest resource name /// The name of the manifest resource being requested /// - /// The manifest resource stream corresponding to with the - /// current suffix applied; or if that is not found, the stream corresponding to . + /// The manifest resource stream corresponding to . /// public static Stream? GetResourceStream(Assembly assembly, Type type, string originalName) { - if (Suffix != string.Empty) - { - try - { - // Resource with suffix has highest priority - if (SameAssemblyOptIn(assembly)) - { - string newName = AppendSuffix(originalName); - Stream? stream = GetResourceStreamHelper(assembly, type, newName); - if (stream != null) - { - return stream; - } - } - } - catch - { - // Ignore failures and continue to try other options - } - - try - { - // Satellite assembly has second priority, using the original name - if (SatelliteAssemblyOptIn(assembly)) - { - AssemblyName assemblyName = assembly.GetName(); - assemblyName.Name += Suffix; - assemblyName.ProcessorArchitecture = ProcessorArchitecture.None; - Assembly satellite = Assembly.Load(assemblyName); - if (satellite != null) - { - Stream? stream = GetResourceStreamHelper(satellite, type, originalName); - if (stream != null) - { - return stream; - } - } - } - } - catch - { - // Ignore failures and continue to try other options - } - } - - // Otherwise fall back to specified assembly and original name requested return assembly.GetManifestResourceStream(type, originalName); } @@ -199,42 +33,11 @@ internal static bool SameAssemblyOptIn(Assembly assembly) /// The type from whose assembly the stream is loaded and whose namespace is used to scope the resource name /// The name of the manifest resource being requested /// - /// The manifest resource stream corresponding to with the - /// current suffix applied; or if that is not found, the stream corresponding to . + /// The manifest resource stream corresponding to . /// public static Stream? GetResourceStream(Type type, string originalName) { return GetResourceStream(type.Module.Assembly, type, originalName); } - - /// - /// Returns an Icon created from a resource stream loaded from the appropriate location according to the current - /// suffix. - /// - /// The type from whose assembly the stream is loaded and whose namespace is used to scope the resource name - /// The name of the manifest resource being requested - /// - /// The icon created from a manifest resource stream corresponding to with the - /// current suffix applied; or if that is not found, the stream corresponding to . - /// - public static Icon CreateIcon(Type type, string originalName) - { - return new Icon(GetResourceStream(type, originalName)!); - } - - /// - /// Returns an Bitmap created from a resource stream loaded from the appropriate location according to the current - /// suffix. - /// - /// The type from whose assembly the stream is loaded and whose namespace is used to scope the resource name - /// The name of the manifest resource being requested - /// - /// The bitmap created from a manifest resource stream corresponding to with the - /// current suffix applied; or if that is not found, the stream corresponding to . - /// - public static Bitmap CreateBitmap(Type type, string originalName) - { - return new Bitmap(GetResourceStream(type, originalName)!); - } } } diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/ToolboxBitmapAttribute.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/ToolboxBitmapAttribute.cs index 2cc414e3e184a..aad75a27f3516 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/ToolboxBitmapAttribute.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/ToolboxBitmapAttribute.cs @@ -28,10 +28,6 @@ public class ToolboxBitmapAttribute : Attribute private static readonly Size s_largeSize = new Size(32, 32); private static readonly Size s_smallSize = new Size(16, 16); - // Used to help cache the last result of BitmapSelector.GetFileName. - private static string? s_lastOriginalFileName; - private static string? s_lastUpdatedFileName; - public ToolboxBitmapAttribute(string imageFile) : this(GetImageFromFile(imageFile, false), GetImageFromFile(imageFile, true)) { _imageFile = imageFile; @@ -168,19 +164,6 @@ public override bool Equals([NotNullWhen(true)] object? value) return b; } - // Cache the last result of BitmapSelector.GetFileName because we commonly load images twice - // in succession from the same file and we don't need to compute the name twice. - private static string? GetFileNameFromBitmapSelector(string originalName) - { - if (originalName != s_lastOriginalFileName) - { - s_lastOriginalFileName = originalName; - s_lastUpdatedFileName = BitmapSelector.GetFileName(originalName); - } - - return s_lastUpdatedFileName; - } - // Just forwards to Image.FromFile eating any non-critical exceptions that may result. private static Image? GetImageFromFile(string? imageFile, bool large, bool scaled = true) { @@ -189,8 +172,6 @@ public override bool Equals([NotNullWhen(true)] object? value) { if (imageFile != null) { - imageFile = GetFileNameFromBitmapSelector(imageFile); - string? ext = Path.GetExtension(imageFile); if (ext != null && string.Equals(ext, ".ico", StringComparison.OrdinalIgnoreCase)) { From 4f3364ebf9237a1454c1bc60a3f742045102bd82 Mon Sep 17 00:00:00 2001 From: Michael Simons Date: Mon, 21 Jun 2021 12:42:57 -0500 Subject: [PATCH 035/107] Build for any RID if building from source (#54223) --- .../Microsoft.NETCore.App.Crossgen2.sfxproj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Crossgen2.sfxproj b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Crossgen2.sfxproj index e9cd505eea169..b039715be97a7 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Crossgen2.sfxproj +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Crossgen2.sfxproj @@ -10,7 +10,8 @@ $(SharedFrameworkName)$(PgoSuffix).$(RuntimeIdentifier) dotnet-crossgen2 crossgen2 - linux-x64;linux-musl-x64;linux-arm;linux-musl-arm;linux-arm64;linux-musl-arm64;osx-x64;osx-arm64;win-x64;win-x86;win-arm64;win-arm + + linux-x64;linux-musl-x64;linux-arm;linux-musl-arm;linux-arm64;linux-musl-arm64;osx-x64;osx-arm64;win-x64;win-x86;win-arm64;win-arm false AddRuntimeFilesToPackage; From 1ccd65f4f887460c019b0908f524b898ed02ce55 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 21 Jun 2021 13:59:28 -0400 Subject: [PATCH 036/107] Special-case no attributes in GetCustomAttributeData (#54405) --- .../src/System/Reflection/CustomAttribute.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs index 1b825f0dc952f..cf35de2462752 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs @@ -237,6 +237,10 @@ private static CustomAttributeType InitCustomAttributeType(RuntimeType parameter private static IList GetCustomAttributes(RuntimeModule module, int tkTarget) { CustomAttributeRecord[] records = GetCustomAttributeRecords(module, tkTarget); + if (records.Length == 0) + { + return Array.Empty(); + } CustomAttributeData[] customAttributes = new CustomAttributeData[records.Length]; for (int i = 0; i < records.Length; i++) From 89b39c50c3778f787bd0f4c29f57a55f73955573 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 21 Jun 2021 13:59:46 -0400 Subject: [PATCH 037/107] Avoid Attribute.InternalGetCustomAttributes overheads when inherit==true is useless (#54402) --- .../src/System/Attribute.CoreCLR.cs | 62 +++++++++++-------- .../System/Reflection/RuntimePropertyInfo.cs | 6 +- 2 files changed, 39 insertions(+), 29 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Attribute.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Attribute.CoreCLR.cs index bd108893d6c22..0779c2401f01d 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Attribute.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Attribute.CoreCLR.cs @@ -25,24 +25,26 @@ private static Attribute[] InternalGetCustomAttributes(PropertyInfo element, Typ if (!inherit) return attributes; + // if this is an index we need to get the parameter types to help disambiguate + Type[] indexParamTypes = GetIndexParameterTypes(element); + PropertyInfo? baseProp = GetParentDefinition(element, indexParamTypes); + if (baseProp == null) + return attributes; + // create the hashtable that keeps track of inherited types Dictionary types = new Dictionary(11); // create an array list to collect all the requested attibutes List attributeList = new List(); - CopyToArrayList(attributeList, attributes, types); - - // if this is an index we need to get the parameter types to help disambiguate - Type[] indexParamTypes = GetIndexParameterTypes(element); - - - PropertyInfo? baseProp = GetParentDefinition(element, indexParamTypes); - while (baseProp != null) + CopyToAttributeList(attributeList, attributes, types); + do { attributes = GetCustomAttributes(baseProp, type, false); AddAttributesToList(attributeList, attributes, types); baseProp = GetParentDefinition(baseProp, indexParamTypes); } + while (baseProp != null); + Attribute[] array = CreateAttributeArrayHelper(type, attributeList.Count); attributeList.CopyTo(array, 0); return array; @@ -123,27 +125,33 @@ private static Attribute[] InternalGetCustomAttributes(EventInfo element, Type t // walk up the hierarchy chain Attribute[] attributes = (Attribute[])element.GetCustomAttributes(type, inherit); - if (inherit) + if (!inherit) { - // create the hashtable that keeps track of inherited types - Dictionary types = new Dictionary(11); - // create an array list to collect all the requested attibutes - List attributeList = new List(); - CopyToArrayList(attributeList, attributes, types); - - EventInfo? baseEvent = GetParentDefinition(element); - while (baseEvent != null) - { - attributes = GetCustomAttributes(baseEvent, type, false); - AddAttributesToList(attributeList, attributes, types); - baseEvent = GetParentDefinition(baseEvent); - } - Attribute[] array = CreateAttributeArrayHelper(type, attributeList.Count); - attributeList.CopyTo(array, 0); - return array; + return attributes; } - else + + EventInfo? baseEvent = GetParentDefinition(element); + if (baseEvent == null) + { return attributes; + } + + // create the hashtable that keeps track of inherited types + // create an array list to collect all the requested attibutes + Dictionary types = new Dictionary(11); + List attributeList = new List(); + CopyToAttributeList(attributeList, attributes, types); + do + { + attributes = GetCustomAttributes(baseEvent, type, false); + AddAttributesToList(attributeList, attributes, types); + baseEvent = GetParentDefinition(baseEvent); + } + while (baseEvent != null); + + Attribute[] array = CreateAttributeArrayHelper(type, attributeList.Count); + attributeList.CopyTo(array, 0); + return array; } [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:UnrecognizedReflectionPattern", @@ -357,7 +365,7 @@ private static bool InternalParamIsDefined(ParameterInfo param, Type type, bool #endregion #region Utility - private static void CopyToArrayList(List attributeList, Attribute[] attributes, Dictionary types) + private static void CopyToAttributeList(List attributeList, Attribute[] attributes, Dictionary types) { for (int i = 0; i < attributes.Length; i++) { diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs index b7e6a58f1fe4b..f01ae3933d0d9 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs @@ -304,9 +304,11 @@ internal ParameterInfo[] GetIndexParametersNoCopy() // Now copy over the parameter info's and change their // owning member info to the current property info. - ParameterInfo[] propParams = new ParameterInfo[numParams]; + ParameterInfo[] propParams = numParams != 0 ? + new ParameterInfo[numParams] : + Array.Empty(); - for (int i = 0; i < numParams; i++) + for (int i = 0; i < propParams.Length; i++) propParams[i] = new RuntimeParameterInfo((RuntimeParameterInfo)methParams![i], this); m_parameters = propParams; From 19b5bd1b588fe19efe835153fd049921bf612204 Mon Sep 17 00:00:00 2001 From: Steve MacLean Date: Mon, 21 Jun 2021 15:10:16 -0400 Subject: [PATCH 038/107] Add Cross DAC documentation (#54498) * Add Cross DAC documentation --- docs/design/features/cross-dac.md | 97 +++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 docs/design/features/cross-dac.md diff --git a/docs/design/features/cross-dac.md b/docs/design/features/cross-dac.md new file mode 100644 index 0000000000000..30becca5be626 --- /dev/null +++ b/docs/design/features/cross-dac.md @@ -0,0 +1,97 @@ +# Cross DAC Notes + +The `crossdac` is a cross-compiled DAC. It is compiled to execute on one platform, but debug a target of a different architecture. + +Our current crossdacs are all: + +- compiled to run on Windows +- Same bitness. (Target and host have the same number of bits. +- target a *nix variant + +The crossdac allow us to use Windows debugging tools to debug dumps from *nix processes. + +## Design + +### Limitations + +- To avoid solving remoting and synchronization issues, the crossdac will not support live processes. Only dump debugging is supported. +- Similar to the DAC, each cross DAC must match its runtime. The DACs are indexed on a symbol server to allow the debuggers to get these as needed. + +### Conditional Code Selection + +This is a simple cross compilation of the DAC, `C++` code. This mean the `HOST_*` and the `TARGET_*` are configured differently. In this context: + +- `HOST` refers to the architecture of the platform that is running the debugger. +- `TARGET` refers to the platform that generated the code dump. + +In general, most code should be conditioned on `TARGET_*` variables. This is because in general we want the `DAC` to behave identically when cross compiled. + +Code must be conditioned on `HOST` when it refers to host needed services. These have typically been thing like file i/o and memory allocation. + +Initial implementation allowed the compiler to find most of these. The strategy was to assume all code should be conditioned on `TARGET` and let the compiler gripe. + +### Type Layout + +The DAC is essentially a memory parsing tool with supporting functionality. The layout of types in the DAC must match the layout of types in the runtime. + +The `C++` standard is not explicit about all layout rules of data structures. Due to its historical evolution from `C`, most structures are arranged in an intuitive easily understood fashion. Newer and more exotic structures are less consistent. + +Experimentation has shown that layout varies in inheritance cases. The DAC does not support general multiple inheritance, so that simplifies things. It does support multiple inheritance with the empty base classes. + +These cases have proven to be problematic: + +- Classes with empty base classes. (I the only issue is with multiple base classes.) + - By default `gcc` use an empty base class optimization to eliminate the 1 byte of space these empty base classes normally consume (alone). + - By default `Windows` compilers do not do this optimization. This is to preserve backward binary compatibility. + - The Windows compilers allow this optimization to be enabled. Our code uses `EMPTY_BASES_DECL` to enable this optimization. It has to be applied to every structure that has multiple base classes or derives from a such a structure. See `__declspec(empty_bases)`. +- Packing of the first member of the derived class. In the case where the base class ended with padding: + - `gcc` compilers reuse the padding for the first member of the derived class. This effectively removes the padding of the base class in the derived class. + - Windows compilers do not remove this padding. + - Our code uses the `DAC_ALIGNAS(a)` macro before the first element of the derived class to force the `gcc` compiler to align that member and keep the base classes padding. + - The `a` parameter is preferentially the base classes typename. + - However, in some cases the compiler will not allow this due to some circular layout issues it causes. In these cases, `a` can refer to a well known type instead. I prefer `int64_t`, `int32_t`, `size_t` ... + +#### DacCompareNativeTypes Usage + +I wrote and used [DacCompareNativeTypes](https://github.com/dotnet/diagnostics/tree/main/src/tests/DacCompareNativeTypes), to locate and identify type layout issues. + +The tool is a bit crude, but it helped get the job done. + +The `libcoreclr.so` has a lot of symbols. This proved very slow. So to expedite things, I compared the `dac` and later the `dbi` libraries for structure layout. This had the advantage of eliminating irrelevant data structures. + +The compilers generate different debug data and different hidden data structures. The tool tries to overlook these. Be aware that not all differences are real. Some data structures are host only so these are expected to be different. + +I usually ran the tool in a debugger so that I could look at other available meta-data the tool keeps. i.e. source file and line number. + +### Missing/Different types + +There are some cases where types are defined by the Target. These types maybe missing or different on the Host. In these cases we define the cross compilation types in `src/coreclr/inc/crosscomp.h`. + +See `T_CRITICAL_SECTION` for a key example. In this case both host and target supported critical sections, but we needed to correctly map the target data structures. So we needed a type defined which was the TARGET's `CRITICAL_SECTION`. + +So the Target's definition was made available for the cross compile. Additionally the macro was created to make sure references which required the Target's definition could be separated from ones which might need the host's definition. + +There is also some defensive programming to make sure these structures accurate. See `T_CRITICAL_SECTION_VALIDATION_MESSAGE` for one example. + +### Out of Process Unwinding + +To fully support native stack processing, we needed a Target unwinder. For this `libunwind` was also cross-compiled. + +See [CMake cross libunwind](https://github.com/dotnet/runtime/blob/0049c629381c5a18e4dadd1038c2bd6b3ae6e3e6/src/coreclr/CMakeLists.txt#L113) + +### DBI + +I use the term `DAC` in this document to refer to both the `DAC` and the `DBI` debug interface. Both were actually cross compiled. Be aware. + +### Build entry point + +The main build systme change is adding the ability to set the Target OS on a Windows build. + +- See [build-runtime.cmd changes](https://github.com/dotnet/runtime/blob/0049c629381c5a18e4dadd1038c2bd6b3ae6e3e6/src/coreclr/build-runtime.cmd#L133-L134) +- See [Subsets.props](https://github.com/dotnet/runtime/blob/0049c629381c5a18e4dadd1038c2bd6b3ae6e3e6/eng/Subsets.props#L191-L197) + +There are also changes to the official build to set these flags package the results and upload to the symbol server. + +### Client changes + +Various changes were required in the DAC clients to consume the new crossdac. These are really out of the scope of this document. \ No newline at end of file From d08c08d2630acc5866225636587f78b35e60fcfe Mon Sep 17 00:00:00 2001 From: Jo Shields Date: Mon, 21 Jun 2021 15:30:03 -0400 Subject: [PATCH 039/107] Add UnixFilePermissions.xml for Mono AOT compilers (#54501) * Add UnixFilePermissions.xml for Mono AOT compilers Ref: https://github.com/dotnet/runtime/issues/53545 Ref: https://github.com/dotnet/sdk/issues/16894 Ref: https://github.com/xamarin/xamarin-macios/pull/11869 Ref: https://github.com/xamarin/xamarin-android/pull/6010 * Only set permissions on !windows --- ...icrosoft.NETCore.App.MonoCrossAOT.UnixFilePermissions.xml | 5 +++++ .../Microsoft.NETCore.App.MonoCrossAOT.sfxproj | 1 + 2 files changed, 6 insertions(+) create mode 100644 src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.MonoCrossAOT.UnixFilePermissions.xml diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.MonoCrossAOT.UnixFilePermissions.xml b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.MonoCrossAOT.UnixFilePermissions.xml new file mode 100644 index 0000000000000..9437f7953f315 --- /dev/null +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.MonoCrossAOT.UnixFilePermissions.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.MonoCrossAOT.sfxproj b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.MonoCrossAOT.sfxproj index 174cf76bb8cba..3c0e7fac2d86b 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.MonoCrossAOT.sfxproj +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.MonoCrossAOT.sfxproj @@ -20,6 +20,7 @@ + From 2bc86980ba50713ced7a2ef12187c299ad6a355a Mon Sep 17 00:00:00 2001 From: Thad House Date: Mon, 21 Jun 2021 12:56:27 -0700 Subject: [PATCH 040/107] Switch MsQuicOpen to MsQuicOpenVersion (#54443) * Switch MsQuicOpen to MsQuicOpenVersion MsQuicOpenVersion is designed so we can update the API without breaking old consumers. The version of MsQuic specified in the readme already has this change, so it is safe to completely switch over. Also switches the API to function pointers, as it was easier then updating the delegate too. * Add version contant --- .../Implementations/MsQuic/Internal/MsQuicApi.cs | 12 +++++++----- .../MsQuic/Interop/MsQuicNativeMethods.cs | 4 ---- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs index 5914cf200c65b..4483920196ada 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs @@ -119,18 +119,19 @@ private MsQuicApi(NativeApi* vtable) internal static bool IsQuicSupported { get; } + private const int MsQuicVersion = 1; + static MsQuicApi() { - // TODO: Consider updating all of these delegates to instead use function pointers. if (NativeLibrary.TryLoad(Interop.Libraries.MsQuic, typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out IntPtr msQuicHandle)) { try { - if (NativeLibrary.TryGetExport(msQuicHandle, "MsQuicOpen", out IntPtr msQuicOpenAddress)) + if (NativeLibrary.TryGetExport(msQuicHandle, "MsQuicOpenVersion", out IntPtr msQuicOpenVersionAddress)) { - MsQuicOpenDelegate msQuicOpen = - Marshal.GetDelegateForFunctionPointer(msQuicOpenAddress); - uint status = msQuicOpen(out NativeApi* vtable); + delegate* unmanaged[Cdecl] msQuicOpenVersion = + (delegate* unmanaged[Cdecl])msQuicOpenVersionAddress; + uint status = msQuicOpenVersion(MsQuicVersion, out NativeApi* vtable); if (MsQuicStatusHelper.SuccessfulStatusCode(status)) { IsQuicSupported = true; @@ -148,6 +149,7 @@ static MsQuicApi() } } + // TODO: Consider updating all of these delegates to instead use function pointers. internal RegistrationOpenDelegate RegistrationOpenDelegate { get; } internal RegistrationCloseDelegate RegistrationCloseDelegate { get; } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs index 3700edbb30395..325ee8170aedc 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs @@ -51,10 +51,6 @@ internal struct NativeApi internal IntPtr DatagramSend; } - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate uint MsQuicOpenDelegate( - out NativeApi* registration); - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint SetContextDelegate( SafeHandle handle, From 213600c17635bf25f812a6a7e6ab53d4fa875883 Mon Sep 17 00:00:00 2001 From: Fan Yang <52458914+fanyang-mono@users.noreply.github.com> Date: Mon, 21 Jun 2021 16:04:39 -0400 Subject: [PATCH 041/107] [Mono] Enable runtime tests to run on Android x64 with interpreter (#54084) * Enable Android x64 with interpreter * Make variable RuntimeVariant available for testenvironment * Pass down runtimeVariant * Verify that tests are running with interpreter * Pass MONO_ENV_OPTIONS value to the app * Set ForceInterpreter to true * Change default value for interp to false * dummy commit * dummy commit 2 * dummy commit * Configure interp for Android * Pass RuntimeVariant down as a parameter * Add issue link * Enable Mono with interpreter on desktop * Disable Android x64 with JIT * Revert hacks to enable all lanes * revert unintentional change * Disable Vector128_1_r* --- .../android-runtime-and-send-to-helix.yml | 3 +- eng/pipelines/runtime-staging.yml | 38 ++++ src/mono/sample/Android/Makefile | 2 + src/tests/Common/testenvironment.proj | 4 +- src/tests/issues.targets | 179 ++++++++---------- src/tests/run.proj | 8 +- 6 files changed, 126 insertions(+), 108 deletions(-) diff --git a/eng/pipelines/common/templates/runtimes/android-runtime-and-send-to-helix.yml b/eng/pipelines/common/templates/runtimes/android-runtime-and-send-to-helix.yml index c45d9f2f5d37b..f42787af8ebd2 100644 --- a/eng/pipelines/common/templates/runtimes/android-runtime-and-send-to-helix.yml +++ b/eng/pipelines/common/templates/runtimes/android-runtime-and-send-to-helix.yml @@ -26,7 +26,7 @@ parameters: steps: - - script: $(Build.SourcesDirectory)/src/tests/build$(scriptExt) /p:LibrariesConfiguration=${{ parameters.buildConfig }} -ci -excludemonofailures os ${{ parameters.osGroup }} ${{ parameters.archType }} $(buildConfigUpper) + - script: $(Build.SourcesDirectory)/src/tests/build$(scriptExt) /p:LibrariesConfiguration=${{ parameters.buildConfig }} -ci -excludemonofailures os ${{ parameters.osGroup }} ${{ parameters.archType }} /p:RuntimeVariant=${{ parameters.runtimeVariant }} $(buildConfigUpper) displayName: Build Tests # Send tests to Helix @@ -40,6 +40,7 @@ steps: coreClrRepoRoot: $(Build.SourcesDirectory)/src/coreclr runtimeFlavorDisplayName: ${{ parameters.runtimeFlavorDisplayName }} shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + runtimeVariant: ${{ parameters.runtimeVariant }} ${{ if eq(variables['System.TeamProject'], 'public') }}: creator: $(Build.DefinitionName) diff --git a/eng/pipelines/runtime-staging.yml b/eng/pipelines/runtime-staging.yml index eaaa9e08fe2c5..5def476efefd6 100644 --- a/eng/pipelines/runtime-staging.yml +++ b/eng/pipelines/runtime-staging.yml @@ -298,6 +298,44 @@ jobs: eq(variables['monoContainsChange'], true), eq(variables['isFullMatrix'], true)) +# +# Build the whole product using Mono for Android and run runtime tests with interpreter +# +- template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/common/global-build-job.yml + helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml + buildConfig: Release + runtimeFlavor: mono + platforms: + - Android_x64 + variables: + - ${{ if and(eq(variables['System.TeamProject'], 'public'), eq(variables['Build.Reason'], 'PullRequest')) }}: + - name: _HelixSource + value: pr/dotnet/runtime/$(Build.SourceBranch) + - ${{ if and(eq(variables['System.TeamProject'], 'public'), ne(variables['Build.Reason'], 'PullRequest')) }}: + - name: _HelixSource + value: ci/dotnet/runtime/$(Build.SourceBranch) + - name: timeoutPerTestInMinutes + value: 60 + - name: timeoutPerTestCollectionInMinutes + value: 180 + jobParameters: + testGroup: innerloop + nameSuffix: AllSubsets_Mono_RuntimeTests + buildArgs: -s mono+libs -c $(_BuildConfig) + timeoutInMinutes: 240 + runtimeVariant: monointerpreter + condition: >- + or( + eq(dependencies.evaluate_paths.outputs['SetPathVars_runtimetests.containsChange'], true), + eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true), + eq(variables['isFullMatrix'], true)) + # extra steps, run tests + extraStepsTemplate: /eng/pipelines/common/templates/runtimes/android-runtime-and-send-to-helix.yml + extraStepsParameters: + creator: dotnet-bot + testRunNamePrefixSuffix: Mono_$(_BuildConfig) # # Build the whole product using Mono for Android and run runtime tests with Android devices diff --git a/src/mono/sample/Android/Makefile b/src/mono/sample/Android/Makefile index 11f1c35df9fb4..6d59ee860fe8b 100644 --- a/src/mono/sample/Android/Makefile +++ b/src/mono/sample/Android/Makefile @@ -3,6 +3,7 @@ MONO_ARCH?=x64 DOTNET := ../../../../dotnet.sh USE_LLVM=true AOT=false +INTERP=false DEPLOY_AND_RUN?=true #If DIAGNOSTIC_PORTS is enabled, RUNTIME_COMPONENTS must also be enabled. @@ -26,6 +27,7 @@ run: /p:Configuration=$(MONO_CONFIG) \ /p:DeployAndRun=$(DEPLOY_AND_RUN) \ /p:ForceAOT=$(AOT) \ + /p:MonoForceInterpreter=$(INTERP) \ /p:UseLLVM=$(USE_LLVM) \ /p:RunActivity=false \ '/p:RuntimeComponents="$(RUNTIME_COMPONENTS)"' \ diff --git a/src/tests/Common/testenvironment.proj b/src/tests/Common/testenvironment.proj index 4dc72fdf06d1c..37392b57df478 100644 --- a/src/tests/Common/testenvironment.proj +++ b/src/tests/Common/testenvironment.proj @@ -193,7 +193,7 @@ <_TestEnvFileLine Include="@(_COMPlusVariable->'set %(Identity)=%(Value)')" /> - <_TestEnvFileLine Include="set MONO_ENV_OPTIONS=--interpreter" Condition="'$(Scenario)' == 'interpreter'" /> + <_TestEnvFileLine Condition="'$(RuntimeVariant)' == 'monointerpreter'" Include="set MONO_ENV_OPTIONS=--interpreter" /> <_TestEnvFileLine Condition="'$(Scenario)' == 'clrinterpreter'" Include="set COMPlus_Interpret=%2A" /> @@ -208,7 +208,7 @@ <_TestEnvFileLine Include="@(_COMPlusVariable->'export %(Identity)=%(Value)')" /> - <_TestEnvFileLine Include="export MONO_ENV_OPTIONS=--interpreter" Condition="'$(Scenario)' == 'interpreter'" /> + <_TestEnvFileLine Condition="'$(RuntimeVariant)' == 'monointerpreter'" Include="export MONO_ENV_OPTIONS=--interpreter" /> <_TestEnvFileLine Condition="'$(RuntimeVariant)' == 'llvmaot'" Include="export MONO_ENV_OPTIONS=--llvm" /> diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 255236a9d346a..46132c96a341b 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -1694,22 +1694,22 @@ needs triage - needs triage + https://github.com/dotnet/runtime/issues/54395 needs triage - needs triage + https://github.com/dotnet/runtime/issues/54396 - needs triage + https://github.com/dotnet/runtime/issues/54374 - needs triage + https://github.com/dotnet/runtime/issues/54381 - needs triage + https://github.com/dotnet/runtime/issues/54374 needs triage @@ -1727,7 +1727,7 @@ needs triage - needs triage + https://github.com/dotnet/runtime/issues/54394 needs triage @@ -1736,16 +1736,16 @@ needs triage - needs triage + https://github.com/dotnet/runtime/issues/54371 needs triage - needs triage + https://github.com/dotnet/runtime/issues/54381 - needs triage + https://github.com/dotnet/runtime/issues/54381 needs triage @@ -1757,13 +1757,13 @@ needs triage - needs triage + https://github.com/dotnet/runtime/issues/54381 - needs triage + https://github.com/dotnet/runtime/issues/54372 - needs triage + https://github.com/dotnet/runtime/issues/54381 needs triage @@ -1775,7 +1775,7 @@ needs triage - needs triage + https://github.com/dotnet/runtime/issues/54372 needs triage @@ -1784,25 +1784,25 @@ needs triage - needs triage + https://github.com/dotnet/runtime/issues/54381 - needs triage + https://github.com/dotnet/runtime/issues/54381 - needs triage + https://github.com/dotnet/runtime/issues/54371 - needs triage + https://github.com/dotnet/runtime/issues/54381 - needs triage + https://github.com/dotnet/runtime/issues/54381 - needs triage + https://github.com/dotnet/runtime/issues/54371 - needs triage + https://github.com/dotnet/runtime/issues/54372 needs triage @@ -1814,19 +1814,19 @@ needs triage - needs triage + https://github.com/dotnet/runtime/issues/54381 needs triage - needs triage + https://github.com/dotnet/runtime/issues/54374 - needs triage + https://github.com/dotnet/runtime/issues/54381 - needs triage + https://github.com/dotnet/runtime/issues/54391 needs triage @@ -1838,10 +1838,10 @@ needs triage - needs triage + https://github.com/dotnet/runtime/issues/54391 - needs triage + https://github.com/dotnet/runtime/issues/54391 needs triage @@ -1868,7 +1868,7 @@ needs triage - needs triage + https://github.com/dotnet/runtime/issues/54381 needs triage @@ -1916,13 +1916,13 @@ needs triage - needs triage + https://github.com/dotnet/runtime/issues/54392 - needs triage + https://github.com/dotnet/runtime/issues/54388 - needs triage + https://github.com/dotnet/runtime/issues/54393 needs triage @@ -1934,7 +1934,7 @@ needs triage - needs triage + https://github.com/dotnet/runtime/issues/54393 needs triage @@ -1943,10 +1943,10 @@ needs triage - needs triage + https://github.com/dotnet/runtime/issues/54393 - needs triage + https://github.com/dotnet/runtime/issues/54388 needs triage @@ -1961,13 +1961,13 @@ needs triage - needs triage + https://github.com/dotnet/runtime/issues/54392 - needs triage + https://github.com/dotnet/runtime/issues/54393 - needs triage + https://github.com/dotnet/runtime/issues/54359 needs triage @@ -1982,16 +1982,16 @@ needs triage - needs triage + https://github.com/dotnet/runtime/issues/54392 needs triage - needs triage + https://github.com/dotnet/runtime/issues/54375 - needs triage + https://github.com/dotnet/runtime/issues/54374 needs triage @@ -2026,17 +2026,11 @@ needs triage - - needs triage - needs triage - - needs triage - - needs triage + https://github.com/dotnet/runtime/issues/54388 needs triage @@ -2044,17 +2038,11 @@ needs triage - - needs triage - - needs triage + https://github.com/dotnet/runtime/issues/54388 - needs triage - - - needs triage + https://github.com/dotnet/runtime/issues/54388 needs triage @@ -2102,10 +2090,7 @@ needs triage - needs triage - - - needs triage + https://github.com/dotnet/runtime/issues/54393 needs triage @@ -2113,14 +2098,8 @@ needs triage - - needs triage - - - needs triage - - needs triage + https://github.com/dotnet/runtime/issues/54359 needs triage @@ -2134,98 +2113,86 @@ needs triage - - needs triage - needs triage - - needs triage - - - needs triage - - needs triage + https://github.com/dotnet/runtime/issues/54399 needs triage - needs triage + https://github.com/dotnet/runtime/issues/54359 - needs triage + https://github.com/dotnet/runtime/issues/54359 - needs triage + https://github.com/dotnet/runtime/issues/54376 - needs triage + https://github.com/dotnet/runtime/issues/54359 - needs triage + https://github.com/dotnet/runtime/issues/54393 - needs triage + https://github.com/dotnet/runtime/issues/54373 needs triage - needs triage + https://github.com/dotnet/runtime/issues/54373 - needs triage + https://github.com/dotnet/runtime/issues/54393 - needs triage + https://github.com/dotnet/runtime/issues/54401 - needs triage + https://github.com/dotnet/runtime/issues/54401 - needs triage + https://github.com/dotnet/runtime/issues/54392 - needs triage + https://github.com/dotnet/runtime/issues/54376 - needs triage + https://github.com/dotnet/runtime/issues/54392 - needs triage + https://github.com/dotnet/runtime/issues/54399 needs triage - needs triage + https://github.com/dotnet/runtime/issues/54376 - needs triage + https://github.com/dotnet/runtime/issues/54392 needs triage - needs triage - - - needs triage + https://github.com/dotnet/runtime/issues/54376 - needs triage + https://github.com/dotnet/runtime/issues/54376 - needs triage + https://github.com/dotnet/runtime/issues/54376 - needs triage + https://github.com/dotnet/runtime/issues/54376 - needs triage + https://github.com/dotnet/runtime/issues/54376 needs triage @@ -2234,19 +2201,25 @@ needs triage - needs triage + https://github.com/dotnet/runtime/issues/54358 needs triage - needs triage + https://github.com/dotnet/runtime/issues/54389 - needs triage + https://github.com/dotnet/runtime/issues/54389 - needs triage + https://github.com/dotnet/runtime/issues/54376 + + + https://github.com/dotnet/runtime/issues/54374e + + + https://github.com/dotnet/runtime/issues/54374 diff --git a/src/tests/run.proj b/src/tests/run.proj index c16a18fe9f362..a64233555d206 100644 --- a/src/tests/run.proj +++ b/src/tests/run.proj @@ -648,6 +648,8 @@ namespace $([System.String]::Copy($(Category)).Replace(".","_").Replace("\",""). armeabi-v7a x86_64 x86 + false + true @@ -683,14 +685,16 @@ namespace $([System.String]::Copy($(Category)).Replace(".","_").Replace("\",""). ProjectName="$(Category)" MonoRuntimeHeaders="$(MicrosoftNetCoreAppRuntimePackDir)/native/include/mono-2.0" StripDebugSymbols="$(StripDebugSymbols)" + ForceInterpreter="$(MonoInterp)" AppDir="$(BuildDir)" OutputDir="$(AppDir)"> - - + + + From dcb03e5689141eb6c55a6643e2b845490947cb4e Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Tue, 22 Jun 2021 09:45:57 +1200 Subject: [PATCH 042/107] HTTP/3: Fix header field length calculation (#54442) --- .../aspnetcore/Http3/QPack/HeaderField.cs | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/QPack/HeaderField.cs b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/QPack/HeaderField.cs index b4b80c53798f4..9cf185918639e 100644 --- a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/QPack/HeaderField.cs +++ b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/QPack/HeaderField.cs @@ -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 System.Diagnostics; +using System.Text; + namespace System.Net.Http.QPack { internal readonly struct HeaderField @@ -11,6 +14,8 @@ internal readonly struct HeaderField public HeaderField(byte[] name, byte[] value) { + Debug.Assert(name.Length > 0); + Name = name; Value = value; } @@ -19,6 +24,20 @@ public HeaderField(byte[] name, byte[] value) public byte[] Value { get; } - public int Length => Name.Length + Value.Length; + public int Length => GetLength(Name.Length, Value.Length); + + public static int GetLength(int nameLength, int valueLength) => nameLength + valueLength + RfcOverhead; + + public override string ToString() + { + if (Name != null) + { + return Encoding.ASCII.GetString(Name) + ": " + Encoding.ASCII.GetString(Value); + } + else + { + return ""; + } + } } } From 0cdd423fbe396275d8faaca0c2fee8b6b6e23674 Mon Sep 17 00:00:00 2001 From: Layomi Akinrinade Date: Mon, 21 Jun 2021 15:08:27 -0700 Subject: [PATCH 043/107] Add code to flush JSON writer after root-level fast-path serialization (#54502) --- .../Serialization/JsonSerializer.Write.Helpers.cs | 1 + .../SerializationLogicTests.cs | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Helpers.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Helpers.cs index 60836120d8907..cd6cd1b163b33 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Helpers.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Helpers.cs @@ -43,6 +43,7 @@ private static void WriteUsingMetadata(Utf8JsonWriter writer, in TValue typedInfo.Options._context?.CanUseSerializationLogic == true) { typedInfo.Serialize(writer, value); + writer.Flush(); } else { diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationLogicTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationLogicTests.cs index 53474be5f1e3f..daa3a4cb71ec9 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationLogicTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationLogicTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.IO; using System.Text.Encodings.Web; using System.Text.Json.Serialization; using Xunit; @@ -111,5 +112,16 @@ public static IEnumerable GetIncompatibleOptions() yield return new object[] { new JsonSerializerOptions(s_compatibleOptions) { DefaultIgnoreCondition = JsonIgnoreCondition.Never } }; yield return new object[] { new JsonSerializerOptions(s_compatibleOptions) { IgnoreReadOnlyFields = true } }; } + + [Fact] + public static void WriterIsFlushedAtRootCall() + { + using MemoryStream ms = new(); + using Utf8JsonWriter writer = new(ms); + + JsonSerializer.Serialize(writer, new HighLowTemps(), SerializationContext.Default.HighLowTemps); + Assert.Equal(18, writer.BytesCommitted); + Assert.Equal(0, writer.BytesPending); + } } } From 44bb2ad691c0e6a744846b853bea1a64dbcbd3b2 Mon Sep 17 00:00:00 2001 From: Oded Hanson Date: Tue, 22 Jun 2021 01:12:18 +0300 Subject: [PATCH 044/107] Dispose transient CFData in Interop.AppleCrypto.X509GetRawData --- .../Interop.X509.cs | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.cs b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.cs index d50ca014c4e3a..7094e90dd8fe4 100644 --- a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.cs +++ b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.cs @@ -55,18 +55,21 @@ internal static byte[] X509GetRawData(SafeSecCertificateHandle cert) out data, out osStatus); - if (ret == 1) + using (data) { - return CoreFoundation.CFGetData(data); + if (ret == 1) + { + return CoreFoundation.CFGetData(data); + } + + if (ret == 0) + { + throw CreateExceptionForOSStatus(osStatus); + } + + Debug.Fail($"Unexpected return value {ret}"); + throw new CryptographicException(); } - - if (ret == 0) - { - throw CreateExceptionForOSStatus(osStatus); - } - - Debug.Fail($"Unexpected return value {ret}"); - throw new CryptographicException(); } internal static SafeSecKeyRefHandle X509GetPrivateKeyFromIdentity(SafeSecIdentityHandle identity) From fb2e61e92572273dbbc356cb1c80e67f82ec143d Mon Sep 17 00:00:00 2001 From: Oded Hanson Date: Tue, 22 Jun 2021 01:14:16 +0300 Subject: [PATCH 045/107] Add name of corrupted certificate to CryptographicException on Mac * Add name of corrupted certificate to CryptographicException on Mac When trying to create a CertificateData out of raw X509 byte array it might throw if the data is corrupted. The existing exception message does not provide any information which might help the user identify the corrupted certificate and fix it. This change, makes a native call to SecCertificateCopySubjectSummary which will provide a human readable summary of the certificate, and will generate an exception message using this string. Co-authored-by: Jeremy Barton --- .../Interop.X509.cs | 30 +++++++++++++++++++ .../entrypoints.c | 1 + .../pal_x509.c | 12 ++++++++ .../pal_x509.h | 10 +++++++ .../Pal.OSX/AppleCertificatePal.cs | 21 ++++++++++++- .../src/Resources/Strings.resx | 3 ++ 6 files changed, 76 insertions(+), 1 deletion(-) diff --git a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.cs b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.cs index 7094e90dd8fe4..b7a8be1ba8a7c 100644 --- a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.cs +++ b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.cs @@ -20,6 +20,11 @@ private static extern int AppleCryptoNative_X509GetRawData( out SafeCFDataHandle cfDataOut, out int pOSStatus); + [DllImport(Libraries.AppleCryptoNative)] + private static extern int AppleCryptoNative_X509GetSubjectSummary( + SafeSecCertificateHandle cert, + out SafeCFStringHandle cfSubjectSummaryOut); + [DllImport(Libraries.AppleCryptoNative)] private static extern int AppleCryptoNative_X509GetPublicKey(SafeSecCertificateHandle cert, out SafeSecKeyRefHandle publicKey, out int pOSStatus); @@ -72,6 +77,31 @@ internal static byte[] X509GetRawData(SafeSecCertificateHandle cert) } } + internal static string? X509GetSubjectSummary(SafeSecCertificateHandle cert) + { + SafeCFStringHandle subjectSummary; + + int ret = AppleCryptoNative_X509GetSubjectSummary( + cert, + out subjectSummary); + + using (subjectSummary) + { + if (ret == 1) + { + return CoreFoundation.CFStringToString(subjectSummary); + } + } + + if (ret == 0) + { + return null; + } + + Debug.Fail($"Unexpected return value {ret}"); + throw new CryptographicException(); + } + internal static SafeSecKeyRefHandle X509GetPrivateKeyFromIdentity(SafeSecIdentityHandle identity) { SafeSecKeyRefHandle key; diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/entrypoints.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/entrypoints.c index ff4df6f41fcb2..1833d4a2161ac 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/entrypoints.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/entrypoints.c @@ -106,6 +106,7 @@ static const Entry s_cryptoAppleNative[] = DllImportEntry(AppleCryptoNative_GetOSStatusForChainStatus) DllImportEntry(AppleCryptoNative_X509ChainSetTrustAnchorCertificates) DllImportEntry(AppleCryptoNative_Pbkdf2) + DllImportEntry(AppleCryptoNative_X509GetSubjectSummary) }; EXTERN_C const void* CryptoAppleResolveDllImport(const char* name); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509.c index 7d3e61f4b5d57..0b6d1f889bc69 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509.c @@ -230,3 +230,15 @@ int32_t AppleCryptoNative_X509GetRawData(SecCertificateRef cert, CFDataRef* ppDa *pOSStatus = *ppDataOut == NULL ? errSecParam : noErr; return (*pOSStatus == noErr); } + +int32_t AppleCryptoNative_X509GetSubjectSummary(SecCertificateRef cert, CFStringRef* ppSummaryOut) +{ + if (ppSummaryOut != NULL) + *ppSummaryOut = NULL; + + if (cert == NULL || ppSummaryOut == NULL) + return kErrorBadInput; + + *ppSummaryOut = SecCertificateCopySubjectSummary(cert); + return (*ppSummaryOut != NULL); +} diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509.h index 28124de10c98d..a0bc58044c3eb 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509.h @@ -72,3 +72,13 @@ ppDataOut: Receives a CFDataRef with the exported blob pOSStatus: Receives the result of SecItemExport */ PALEXPORT int32_t AppleCryptoNative_X509GetRawData(SecCertificateRef cert, CFDataRef* ppDataOut, int32_t* pOSStatus); + +/* +Extract a string that contains a human-readable summary of the contents of the certificate + +Returns 1 on success, 0 on failure, any other value indicates invalid state. + +Output: +ppSummaryOut: Receives a CFDataRef with the exported blob +*/ +PALEXPORT int32_t AppleCryptoNative_X509GetSubjectSummary(SecCertificateRef cert, CFStringRef* ppSummaryOut); diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.cs index cb949b8a93f7a..07959c3e77b5a 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.cs @@ -399,7 +399,26 @@ private void EnsureCertData() return; Debug.Assert(!_certHandle.IsInvalid); - _certData = new CertificateData(Interop.AppleCrypto.X509GetRawData(_certHandle)); + string? subjectSummary = Interop.AppleCrypto.X509GetSubjectSummary(_certHandle); + + try + { + _certData = new CertificateData(Interop.AppleCrypto.X509GetRawData(_certHandle)); + } + catch (CryptographicException e) + { + if (subjectSummary is null) + { + throw; + } + + string message = SR.Format( + SR.Cryptography_X509_CertificateCorrupted, + subjectSummary); + + throw new CryptographicException(message, e); + } + _readCertData = true; } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Resources/Strings.resx b/src/libraries/System.Security.Cryptography.X509Certificates/src/Resources/Strings.resx index a5c33a43b40b8..322e2bab91d0c 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Resources/Strings.resx +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Resources/Strings.resx @@ -331,6 +331,9 @@ The key contents do not contain a PEM, the content is malformed, or the key does not match the certificate. + + Certificate '{0}' is corrupted. + Enumeration has not started. Call MoveNext. From 066894e0b74fc5ecbff95fe37207caa269d5695d Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Mon, 21 Jun 2021 19:36:51 -0400 Subject: [PATCH 046/107] SYSLIB0026: Obsolete mutable X509 certificate APIs --- docs/project/list-of-diagnostics.md | 1 + src/libraries/Common/src/System/Obsoletions.cs | 3 +++ .../UnitTests/SslAuthenticationOptionsTests.cs | 4 ++-- .../tests/Pkcs12/Pkcs12SafeContentsTests.cs | 2 +- ...ystem.Security.Cryptography.X509Certificates.cs | 14 ++++++++++++++ ...m.Security.Cryptography.X509Certificates.csproj | 3 ++- ...m.Security.Cryptography.X509Certificates.csproj | 4 +++- .../X509Certificates/X509Certificate.cs | 12 ++++++++++++ .../X509Certificates/X509Certificate2.cs | 7 +++++++ ...rity.Cryptography.X509Certificates.Tests.csproj | 1 + .../tests/MembershipConditionTests.cs | 2 +- .../tests/PermissionTests.cs | 2 +- 12 files changed, 48 insertions(+), 7 deletions(-) diff --git a/docs/project/list-of-diagnostics.md b/docs/project/list-of-diagnostics.md index 46092b5528018..fc946ddb03b08 100644 --- a/docs/project/list-of-diagnostics.md +++ b/docs/project/list-of-diagnostics.md @@ -80,6 +80,7 @@ The PR that reveals the implementation of the ` { return new X509Certificate(); }; + LocalCertificateSelectionCallback callback = (sender, host, localCertificates, remoteCertificate, issuers) => default; _clientOptions.LocalCertificateSelectionCallback = callback; Assert.Equal(callback, _clientOptions.LocalCertificateSelectionCallback); @@ -109,7 +109,7 @@ public void ServerCertificate_Get_Set_Succeeds() _serverOptions.ServerCertificate = null; Assert.Null(_serverOptions.ServerCertificate); - X509Certificate cert = new X509Certificate(); + X509Certificate cert = new X509Certificate2(stackalloc byte[0]); _serverOptions.ServerCertificate = cert; Assert.Equal(cert, _serverOptions.ServerCertificate); diff --git a/src/libraries/System.Security.Cryptography.Pkcs/tests/Pkcs12/Pkcs12SafeContentsTests.cs b/src/libraries/System.Security.Cryptography.Pkcs/tests/Pkcs12/Pkcs12SafeContentsTests.cs index 9acecd8c695ca..1e1bf33769ff1 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/tests/Pkcs12/Pkcs12SafeContentsTests.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/tests/Pkcs12/Pkcs12SafeContentsTests.cs @@ -74,7 +74,7 @@ public static void AddCertificateDisallowsNull(bool forReadOnly) public static void AddCertificateDisallowedInReadOnly() { Pkcs12SafeContents contents = MakeReadonly(new Pkcs12SafeContents()); - X509Certificate2 cert = new X509Certificate2(); + X509Certificate2 cert = new X509Certificate2(stackalloc byte[0]); Assert.Throws(() => contents.AddCertificate(cert)); } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.cs b/src/libraries/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.cs index 0bf7a6e4b2236..e3ade1a368c51 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.cs @@ -147,6 +147,7 @@ public override void CopyFrom(System.Security.Cryptography.AsnEncodedData asnEnc } public partial class X509Certificate : System.IDisposable, System.Runtime.Serialization.IDeserializationCallback, System.Runtime.Serialization.ISerializable { + [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] public X509Certificate() { } public X509Certificate(byte[] data) { } [System.CLSCompliantAttribute(false)] @@ -200,13 +201,19 @@ protected virtual void Dispose(bool disposing) { } public virtual string GetRawCertDataString() { throw null; } public virtual byte[] GetSerialNumber() { throw null; } public virtual string GetSerialNumberString() { throw null; } + [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] public virtual void Import(byte[] rawData) { } [System.CLSCompliantAttribute(false)] + [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] public virtual void Import(byte[] rawData, System.Security.SecureString? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags) { } + [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] public virtual void Import(byte[] rawData, string? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags) { } + [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] public virtual void Import(string fileName) { } [System.CLSCompliantAttribute(false)] + [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] public virtual void Import(string fileName, System.Security.SecureString? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags) { } + [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] public virtual void Import(string fileName, string? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags) { } public virtual void Reset() { } void System.Runtime.Serialization.IDeserializationCallback.OnDeserialization(object? sender) { } @@ -217,6 +224,7 @@ void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Ser } public partial class X509Certificate2 : System.Security.Cryptography.X509Certificates.X509Certificate { + [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] public X509Certificate2() { } public X509Certificate2(byte[] rawData) { } [System.CLSCompliantAttribute(false)] @@ -265,13 +273,19 @@ public X509Certificate2(string fileName, string? password, System.Security.Crypt public System.Security.Cryptography.ECDiffieHellman? GetECDiffieHellmanPrivateKey() { throw null; } public System.Security.Cryptography.ECDiffieHellman? GetECDiffieHellmanPublicKey() { throw null; } public string GetNameInfo(System.Security.Cryptography.X509Certificates.X509NameType nameType, bool forIssuer) { throw null; } + [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] public override void Import(byte[] rawData) { } [System.CLSCompliantAttribute(false)] + [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] public override void Import(byte[] rawData, System.Security.SecureString? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags) { } + [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] public override void Import(byte[] rawData, string? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags) { } + [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] public override void Import(string fileName) { } [System.CLSCompliantAttribute(false)] + [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] public override void Import(string fileName, System.Security.SecureString? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags) { } + [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] public override void Import(string fileName, string? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags) { } public override void Reset() { } public override string ToString() { throw null; } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.csproj b/src/libraries/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.csproj index d670fc85a01e4..66188cc890c48 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.csproj +++ b/src/libraries/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.csproj @@ -2,6 +2,7 @@ $(NetCoreAppCurrent) enable + $(NoWarn);SYSLIB0026 @@ -15,4 +16,4 @@ - \ No newline at end of file + diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj b/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj index 458fa92770709..2c0921f9fcd93 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj @@ -6,7 +6,7 @@ enable - $(NoWarn);CS8769 + $(NoWarn);CS8769;SYSLIB0026 SR.SystemSecurityCryptographyX509Certificates_PlatformNotSupported @@ -22,6 +22,8 @@ Link="Common\Microsoft\Win32\SafeHandles\SafeHandleCache.cs" /> + password, } [System.CLSCompliantAttribute(false)] +#pragma warning disable SYSLIB0026 public X509Certificate(string fileName, SecureString? password, X509KeyStorageFlags keyStorageFlags) : this() +#pragma warning restore SYSLIB0026 { if (fileName == null) throw new ArgumentNullException(nameof(fileName)); @@ -195,7 +199,9 @@ public X509Certificate(X509Certificate cert) } } +#pragma warning disable SYSLIB0026 public X509Certificate(SerializationInfo info, StreamingContext context) : this() +#pragma warning restore SYSLIB0026 { throw new PlatformNotSupportedException(); } @@ -569,33 +575,39 @@ public virtual string ToString(bool fVerbose) return sb.ToString(); } + [Obsolete(Obsoletions.X509CertificateImmutableMessage, DiagnosticId = Obsoletions.X509CertificateImmutableDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public virtual void Import(byte[] rawData) { throw new PlatformNotSupportedException(SR.NotSupported_ImmutableX509Certificate); } + [Obsolete(Obsoletions.X509CertificateImmutableMessage, DiagnosticId = Obsoletions.X509CertificateImmutableDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public virtual void Import(byte[] rawData, string? password, X509KeyStorageFlags keyStorageFlags) { throw new PlatformNotSupportedException(SR.NotSupported_ImmutableX509Certificate); } [System.CLSCompliantAttribute(false)] + [Obsolete(Obsoletions.X509CertificateImmutableMessage, DiagnosticId = Obsoletions.X509CertificateImmutableDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public virtual void Import(byte[] rawData, SecureString? password, X509KeyStorageFlags keyStorageFlags) { throw new PlatformNotSupportedException(SR.NotSupported_ImmutableX509Certificate); } + [Obsolete(Obsoletions.X509CertificateImmutableMessage, DiagnosticId = Obsoletions.X509CertificateImmutableDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public virtual void Import(string fileName) { throw new PlatformNotSupportedException(SR.NotSupported_ImmutableX509Certificate); } + [Obsolete(Obsoletions.X509CertificateImmutableMessage, DiagnosticId = Obsoletions.X509CertificateImmutableDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public virtual void Import(string fileName, string? password, X509KeyStorageFlags keyStorageFlags) { throw new PlatformNotSupportedException(SR.NotSupported_ImmutableX509Certificate); } [System.CLSCompliantAttribute(false)] + [Obsolete(Obsoletions.X509CertificateImmutableMessage, DiagnosticId = Obsoletions.X509CertificateImmutableDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public virtual void Import(string fileName, SecureString? password, X509KeyStorageFlags keyStorageFlags) { throw new PlatformNotSupportedException(SR.NotSupported_ImmutableX509Certificate); diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs index 067dbdd650f98..d4ef8220853cd 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs @@ -43,6 +43,7 @@ public override void Reset() base.Reset(); } + [Obsolete(Obsoletions.X509CertificateImmutableMessage, DiagnosticId = Obsoletions.X509CertificateImmutableDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public X509Certificate2() : base() { @@ -633,33 +634,39 @@ public override string ToString(bool verbose) return sb.ToString(); } + [Obsolete(Obsoletions.X509CertificateImmutableMessage, DiagnosticId = Obsoletions.X509CertificateImmutableDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public override void Import(byte[] rawData) { base.Import(rawData); } + [Obsolete(Obsoletions.X509CertificateImmutableMessage, DiagnosticId = Obsoletions.X509CertificateImmutableDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public override void Import(byte[] rawData, string? password, X509KeyStorageFlags keyStorageFlags) { base.Import(rawData, password, keyStorageFlags); } [System.CLSCompliantAttribute(false)] + [Obsolete(Obsoletions.X509CertificateImmutableMessage, DiagnosticId = Obsoletions.X509CertificateImmutableDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public override void Import(byte[] rawData, SecureString? password, X509KeyStorageFlags keyStorageFlags) { base.Import(rawData, password, keyStorageFlags); } + [Obsolete(Obsoletions.X509CertificateImmutableMessage, DiagnosticId = Obsoletions.X509CertificateImmutableDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public override void Import(string fileName) { base.Import(fileName); } + [Obsolete(Obsoletions.X509CertificateImmutableMessage, DiagnosticId = Obsoletions.X509CertificateImmutableDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public override void Import(string fileName, string? password, X509KeyStorageFlags keyStorageFlags) { base.Import(fileName, password, keyStorageFlags); } [System.CLSCompliantAttribute(false)] + [Obsolete(Obsoletions.X509CertificateImmutableMessage, DiagnosticId = Obsoletions.X509CertificateImmutableDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public override void Import(string fileName, SecureString? password, X509KeyStorageFlags keyStorageFlags) { base.Import(fileName, password, keyStorageFlags); diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj b/src/libraries/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj index a3ecd6dbeaaf8..ee68bbc75bcc3 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj @@ -4,6 +4,7 @@ $(DefineConstants);HAVE_THUMBPRINT_OVERLOADS $(DefineConstants);Unix true + $(NoWarn);SYSLIB0026 $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Android;$(NetCoreAppCurrent)-Browser;$(NetCoreAppCurrent)-OSX;$(NetCoreAppCurrent)-iOS;$(NetCoreAppCurrent)-tvOS diff --git a/src/libraries/System.Security.Permissions/tests/MembershipConditionTests.cs b/src/libraries/System.Security.Permissions/tests/MembershipConditionTests.cs index d2714f5a3b603..62703d868cc33 100644 --- a/src/libraries/System.Security.Permissions/tests/MembershipConditionTests.cs +++ b/src/libraries/System.Security.Permissions/tests/MembershipConditionTests.cs @@ -82,7 +82,7 @@ public static void HashMembershipConditionCallMethods() [SkipOnPlatform(TestPlatforms.Browser, "System.Security.Cryptography.X509Certificates is not supported on this platform.")] public static void PublisherMembershipConditionCallMethods() { - PublisherMembershipCondition pmc = new PublisherMembershipCondition(new System.Security.Cryptography.X509Certificates.X509Certificate()); + PublisherMembershipCondition pmc = new PublisherMembershipCondition(new System.Security.Cryptography.X509Certificates.X509Certificate2(stackalloc byte[0])); bool check = pmc.Check(new Evidence()); IMembershipCondition obj = pmc.Copy(); check = pmc.Equals(new object()); diff --git a/src/libraries/System.Security.Permissions/tests/PermissionTests.cs b/src/libraries/System.Security.Permissions/tests/PermissionTests.cs index af714344d0dbd..daa9d8ac55ae6 100644 --- a/src/libraries/System.Security.Permissions/tests/PermissionTests.cs +++ b/src/libraries/System.Security.Permissions/tests/PermissionTests.cs @@ -213,7 +213,7 @@ public static void PrincipalPermissionCallMethods() [SkipOnPlatform(TestPlatforms.Browser, "System.Security.Cryptography.X509Certificates is not supported on this platform.")] public static void PublisherIdentityPermissionCallMethods() { - PublisherIdentityPermission pip = new PublisherIdentityPermission(new System.Security.Cryptography.X509Certificates.X509Certificate()); + PublisherIdentityPermission pip = new PublisherIdentityPermission(new System.Security.Cryptography.X509Certificates.X509Certificate2(stackalloc byte[0])); PublisherIdentityPermission pip2 = new PublisherIdentityPermission(new Permissions.PermissionState()); IPermission ip = pip.Copy(); IPermission ip2 = pip.Intersect(ip); From 2a43c07bb82113a029090730d8ca001a68b22c05 Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Mon, 21 Jun 2021 18:10:58 -0700 Subject: [PATCH 047/107] Disable mainv1 and mainv2 for GCStress due to #54203 (#54514) --- src/tests/readytorun/tests/mainv1.csproj | 2 ++ src/tests/readytorun/tests/mainv2.csproj | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/tests/readytorun/tests/mainv1.csproj b/src/tests/readytorun/tests/mainv1.csproj index e8546a30d5b21..032c89953606e 100644 --- a/src/tests/readytorun/tests/mainv1.csproj +++ b/src/tests/readytorun/tests/mainv1.csproj @@ -3,6 +3,8 @@ exe BuildAndRun false + + true diff --git a/src/tests/readytorun/tests/mainv2.csproj b/src/tests/readytorun/tests/mainv2.csproj index 4ca1d852b13d7..a60c79cf464da 100644 --- a/src/tests/readytorun/tests/mainv2.csproj +++ b/src/tests/readytorun/tests/mainv2.csproj @@ -3,6 +3,8 @@ exe BuildAndRun false + + true From bea8d9564c4e63e204efa97bcd584fb0099c72b0 Mon Sep 17 00:00:00 2001 From: Prashanth Govindarajan Date: Mon, 21 Jun 2021 18:22:30 -0700 Subject: [PATCH 048/107] More Parse tests for double, single and Half (#50394) * Test ibm-fpgen locally for validation * sq --- THIRD-PARTY-NOTICES.TXT | 2 +- eng/Version.Details.xml | 4 ++ eng/Versions.props | 1 + .../tests/System.Runtime.Tests.csproj | 3 +- .../tests/System/DoubleTests.cs | 65 +++++++++++++++++++ 5 files changed, 73 insertions(+), 2 deletions(-) diff --git a/THIRD-PARTY-NOTICES.TXT b/THIRD-PARTY-NOTICES.TXT index a877e8fb7abfb..14c806c5ca3e4 100644 --- a/THIRD-PARTY-NOTICES.TXT +++ b/THIRD-PARTY-NOTICES.TXT @@ -680,7 +680,7 @@ worldwide. This software is distributed without any warranty. See . -License for fastmod (https://github.com/lemire/fastmod) +License for fastmod (https://github.com/lemire/fastmod) and ibm-fpgen (https://github.com/nigeltao/parse-number-fxx-test-data) -------------------------------------- Copyright 2018 Daniel Lemire diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 2485b69e1147a..f1ef354b76c66 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -214,5 +214,9 @@ https://github.com/dotnet/hotreload-utils 25b814e010cd4796cedfbcce72a274c26928f496 + + https://github.com/dotnet/runtime-assets + 8d7b898b96cbdb868cac343e938173105287ed9e + diff --git a/eng/Versions.props b/eng/Versions.props index 493ee882e5955..b62b9b4854cfc 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -110,6 +110,7 @@ 4.5.0 6.0.0-preview.6.21314.1 + 6.0.0-beta.21314.1 6.0.0-beta.21307.1 6.0.0-beta.21307.1 6.0.0-beta.21307.1 diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj index 2a68cc12651e1..2ca6739e9c6c1 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj @@ -1,4 +1,4 @@ - + true $(NoWarn),1718,SYSLIB0013 @@ -282,6 +282,7 @@ + diff --git a/src/libraries/System.Runtime/tests/System/DoubleTests.cs b/src/libraries/System.Runtime/tests/System/DoubleTests.cs index cfe3690e588de..a80eb05598c7e 100644 --- a/src/libraries/System.Runtime/tests/System/DoubleTests.cs +++ b/src/libraries/System.Runtime/tests/System/DoubleTests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; +using System.IO; using Xunit; #pragma warning disable xUnit1025 // reporting duplicate test cases due to not distinguishing 0.0 from -0.0, NaN from -NaN @@ -346,6 +347,70 @@ public static void Parse(string value, NumberStyles style, IFormatProvider provi } } + internal static string SplitPairs(string input) + { + string[] splitPairs = input.Split('-'); + string ret = ""; + foreach (var pair in splitPairs) + { + string reversedPair = Reverse(pair); + ret += reversedPair; + } + + return ret; + } + + internal static string Reverse(string s) + { + char[] charArray = s.ToCharArray(); + Array.Reverse(charArray); + return new string(charArray); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + public static void ParsePatterns() + { + string path = Directory.GetCurrentDirectory(); + using (FileStream file = new FileStream(Path.Combine(path, "ibm-fpgen.txt"), FileMode.Open)) + { + using (var streamReader = new StreamReader(file)) + { + string line = streamReader.ReadLine(); + while (line != null) + { + string[] data = line.Split(' '); + string inputHalfBytes = data[0]; + string inputFloatBytes = data[1]; + string inputDoubleBytes = data[2]; + string correctValue = data[3]; + + double doubleValue = double.Parse(correctValue, NumberFormatInfo.InvariantInfo); + string doubleBytes = BitConverter.ToString(BitConverter.GetBytes(doubleValue)); + float floatValue = float.Parse(correctValue, NumberFormatInfo.InvariantInfo); + string floatBytes = BitConverter.ToString(BitConverter.GetBytes(floatValue)); + Half halfValue = Half.Parse(correctValue, NumberFormatInfo.InvariantInfo); + string halfBytes = BitConverter.ToString(BitConverter.GetBytes(halfValue)); + + doubleBytes = SplitPairs(doubleBytes); + floatBytes = SplitPairs(floatBytes); + halfBytes = SplitPairs(halfBytes); + + if (BitConverter.IsLittleEndian) + { + doubleBytes = Reverse(doubleBytes); + floatBytes = Reverse(floatBytes); + halfBytes = Reverse(halfBytes); + } + + Assert.Equal(doubleBytes, inputDoubleBytes); + Assert.Equal(floatBytes, inputFloatBytes); + Assert.Equal(halfBytes, inputHalfBytes); + line = streamReader.ReadLine(); + } + } + } + } + public static IEnumerable Parse_Invalid_TestData() { NumberStyles defaultStyle = NumberStyles.Float; From a2f58177b9ca0998ab6adfde98e5ac3fdd946225 Mon Sep 17 00:00:00 2001 From: Manish Godse <61718172+mangod9@users.noreply.github.com> Date: Mon, 21 Jun 2021 18:52:40 -0700 Subject: [PATCH 049/107] removing more crossgen code from being built. (#54458) Should improve build times further. --- src/coreclr/md/enc/CMakeLists.txt | 4 - src/coreclr/md/runtime/CMakeLists.txt | 4 - src/coreclr/utilcode/CMakeLists.txt | 11 - src/coreclr/vm/crossgen/CMakeLists.txt | 283 ------------------------- 4 files changed, 302 deletions(-) delete mode 100644 src/coreclr/vm/crossgen/CMakeLists.txt diff --git a/src/coreclr/md/enc/CMakeLists.txt b/src/coreclr/md/enc/CMakeLists.txt index 6bd2518d868c9..d4abb371ffbc4 100644 --- a/src/coreclr/md/enc/CMakeLists.txt +++ b/src/coreclr/md/enc/CMakeLists.txt @@ -62,10 +62,6 @@ add_library_clr(mdruntimerw-dbi ${MDRUNTIMERW_SOURCES}) set_target_properties(mdruntimerw-dbi PROPERTIES DBI_COMPONENT TRUE) target_precompile_headers(mdruntimerw-dbi PRIVATE stdafx.h) -add_library_clr(mdruntimerw_crossgen ${MDRUNTIMERW_SOURCES}) -set_target_properties(mdruntimerw_crossgen PROPERTIES CROSSGEN_COMPONENT TRUE) -target_precompile_headers(mdruntimerw_crossgen PRIVATE stdafx.h) - add_library_clr(mdruntimerw_ppdb ${MDRUNTIMERW_SOURCES}) target_compile_definitions(mdruntimerw_ppdb PRIVATE FEATURE_METADATA_EMIT_ALL FEATURE_METADATA_EMIT_PORTABLE_PDB) target_precompile_headers(mdruntimerw_ppdb PRIVATE stdafx.h) diff --git a/src/coreclr/md/runtime/CMakeLists.txt b/src/coreclr/md/runtime/CMakeLists.txt index 6ff49d3e803e2..3e1fc8eda75c1 100644 --- a/src/coreclr/md/runtime/CMakeLists.txt +++ b/src/coreclr/md/runtime/CMakeLists.txt @@ -59,10 +59,6 @@ add_library_clr(mdruntime-dbi ${MDRUNTIME_SOURCES}) set_target_properties(mdruntime-dbi PROPERTIES DBI_COMPONENT TRUE) target_precompile_headers(mdruntime-dbi PRIVATE stdafx.h) -add_library_clr(mdruntime_crossgen ${MDRUNTIME_SOURCES}) -set_target_properties(mdruntime_crossgen PROPERTIES CROSSGEN_COMPONENT TRUE) -target_precompile_headers(mdruntime_crossgen PRIVATE stdafx.h) - add_library_clr(mdruntime_ppdb ${MDRUNTIME_SOURCES}) target_compile_definitions(mdruntime_ppdb PRIVATE FEATURE_METADATA_EMIT_ALL FEATURE_METADATA_EMIT_PORTABLE_PDB) target_precompile_headers(mdruntime_ppdb PRIVATE stdafx.h) diff --git a/src/coreclr/utilcode/CMakeLists.txt b/src/coreclr/utilcode/CMakeLists.txt index fec22cf9dce38..1ae433adbfd89 100644 --- a/src/coreclr/utilcode/CMakeLists.txt +++ b/src/coreclr/utilcode/CMakeLists.txt @@ -76,10 +76,6 @@ set(UTILCODE_DAC_SOURCES hostimpl.cpp ) -set(UTILCODE_CROSSGEN_SOURCES - ${UTILCODE_COMMON_SOURCES} - hostimpl.cpp -) set(UTILCODE_STATICNOHOST_SOURCES ${UTILCODE_COMMON_SOURCES} @@ -90,7 +86,6 @@ set (UTILCODE_DEPENDENCIES eventing_headers) convert_to_absolute_path(UTILCODE_SOURCES ${UTILCODE_SOURCES}) convert_to_absolute_path(UTILCODE_DAC_SOURCES ${UTILCODE_DAC_SOURCES}) -convert_to_absolute_path(UTILCODE_CROSSGEN_SOURCES ${UTILCODE_CROSSGEN_SOURCES}) convert_to_absolute_path(UTILCODE_STATICNOHOST_SOURCES ${UTILCODE_STATICNOHOST_SOURCES}) add_library_clr(utilcode_dac STATIC ${UTILCODE_DAC_SOURCES}) @@ -98,11 +93,9 @@ add_library_clr(utilcode_obj OBJECT ${UTILCODE_SOURCES}) add_library(utilcode INTERFACE) target_sources(utilcode INTERFACE $) add_library_clr(utilcodestaticnohost STATIC ${UTILCODE_STATICNOHOST_SOURCES}) -add_library_clr(utilcode_crossgen STATIC ${UTILCODE_CROSSGEN_SOURCES}) if(CLR_CMAKE_HOST_UNIX) target_link_libraries(utilcodestaticnohost nativeresourcestring) - target_link_libraries(utilcode_crossgen nativeresourcestring) target_link_libraries(utilcode_dac nativeresourcestring) target_link_libraries(utilcode INTERFACE nativeresourcestring) add_dependencies(utilcode_dac coreclrpal) @@ -114,20 +107,16 @@ if(CLR_CMAKE_HOST_WIN32) target_compile_definitions(utilcodestaticnohost PRIVATE _CRTIMP=) # use static version of crt link_natvis_sources_for_target(utilcodestaticnohost INTERFACE utilcode.natvis) - link_natvis_sources_for_target(utilcode_crossgen INTERFACE utilcode.natvis) link_natvis_sources_for_target(utilcode_dac INTERFACE utilcode.natvis) link_natvis_sources_for_target(utilcode INTERFACE utilcode.natvis) endif(CLR_CMAKE_HOST_WIN32) set_target_properties(utilcode_dac PROPERTIES DAC_COMPONENT TRUE) -set_target_properties(utilcode_crossgen PROPERTIES CROSSGEN_COMPONENT TRUE) target_compile_definitions(utilcode_dac PRIVATE SELF_NO_HOST) target_compile_definitions(utilcodestaticnohost PRIVATE SELF_NO_HOST) add_dependencies(utilcode_dac ${UTILCODE_DEPENDENCIES}) add_dependencies(utilcode_obj ${UTILCODE_DEPENDENCIES}) -add_dependencies(utilcode_crossgen ${UTILCODE_DEPENDENCIES}) add_dependencies(utilcodestaticnohost ${UTILCODE_DEPENDENCIES}) target_precompile_headers(utilcode_dac PRIVATE [["stdafx.h"]]) target_precompile_headers(utilcode_obj PRIVATE [["stdafx.h"]]) -target_precompile_headers(utilcode_crossgen PRIVATE [["stdafx.h"]]) target_precompile_headers(utilcodestaticnohost PRIVATE [["stdafx.h"]]) diff --git a/src/coreclr/vm/crossgen/CMakeLists.txt b/src/coreclr/vm/crossgen/CMakeLists.txt deleted file mode 100644 index fa58514532570..0000000000000 --- a/src/coreclr/vm/crossgen/CMakeLists.txt +++ /dev/null @@ -1,283 +0,0 @@ -set(VM_CROSSGEN_SOURCES - ../appdomain.cpp - ../array.cpp - ../assembly.cpp - ../assemblyloadcontext.cpp - ../assemblyspec.cpp - ../baseassemblyspec.cpp - ../binder.cpp - ../bundle.cpp - ../callconvbuilder.cpp - ../castcache.cpp - ../ceeload.cpp - ../ceemain.cpp - ../class.cpp - ../classhash.cpp - ../classlayoutinfo.cpp - ../clrex.cpp - ../clsload.cpp - ../codeman.cpp - ../codeversion.cpp - ../comdelegate.cpp - ../compile.cpp - ../contractimpl.cpp - ../coreassemblyspec.cpp - ../corebindresult.cpp - ../corelib.cpp - ../crossgencompile.cpp - ../custommarshalerinfo.cpp - ../dataimage.cpp - ../debuginfostore.cpp - ../decodemd.cpp - ../dllimport.cpp - ../dllimportcallback.cpp - ../domainfile.cpp - ../ecall.cpp - ../eeconfig.cpp - ../eehash.cpp - ../eetwain.cpp - ../excep.cpp - ../field.cpp - ../fieldmarshaler.cpp - ../formattype.cpp - ../frames.cpp - ../gcinfodecoder.cpp - ../genericdict.cpp - ../generics.cpp - ../genmeth.cpp - ../hash.cpp - ../ilinstrumentation.cpp - ../ilmarshalers.cpp - ../ilstubcache.cpp - ../ilstubresolver.cpp - ../inlinetracking.cpp - ../instmethhash.cpp - ../interoputil.cpp - ../invokeutil.cpp - ../jithost.cpp - ../jitinterface.cpp - ../loaderallocator.cpp - ../memberload.cpp - ../method.cpp - ../methodimpl.cpp - ../methodtable.cpp - ../methodtablebuilder.cpp - ../mlinfo.cpp - ../nativeimage.cpp - ../olevariant.cpp - ../pefile.cpp - ../peimage.cpp - ../peimagelayout.cpp - ../pendingload.cpp - ../precode.cpp - ../sigformat.cpp - ../siginfo.cpp - ../simplerwlock.cpp - ../spinlock.cpp - ../stackingallocator.cpp - ../stubcache.cpp - ../stubgen.cpp - ../stublink.cpp - ../tailcallhelp.cpp - ../typectxt.cpp - ../typedesc.cpp - ../typeequivalencehash.cpp - ../typehandle.cpp - ../typehash.cpp - ../typeparse.cpp - ../typestring.cpp - ../util.cpp - ../vars.cpp - ../versionresilienthashcode.cpp - ../zapsig.cpp -) - -set(VM_CROSSGEN_HEADERS - ../appdomain.hpp - ../appdomain.inl - ../array.h - ../assembly.hpp - ../assemblyloadcontext.h - ../assemblyspec.hpp - ../assemblyspecbase.h - ../baseassemblyspec.h - ../baseassemblyspec.inl - ../binder.h - ../ceeload.h - ../ceeload.inl - ../ceemain.h - ../class.h - ../class.inl - ../classhash.h - ../clrex.h - ../clsload.hpp - ../clsload.inl - ../codeman.h - ../codeversion.h - ../comdelegate.h - ../compile.h - ../contractimpl.h - ../corelib.h - ../custommarshalerinfo.h - ../dataimage.h - ../debuginfostore.h - ../decodemd.h - ../dllimport.h - ../dllimportcallback.h - ../domainfile.h - ../domainfile.inl - ../ecall.h - ../eeconfig.h - ../eehash.h - ../eehash.inl - ../excep.h - ../field.h - ../fieldmarshaler.h - ../genericdict.h - ../generics.h - ../hash.h - ../ilinstrumentation.h - ../ilmarshalers.h - ../ilstubcache.h - ../ilstubresolver.h - ../inlinetracking.h - ../instmethhash.h - ../interoputil.h - ../invokeutil.h - ../jithost.h - ../jitinterface.h - ../loaderallocator.hpp - ../loaderallocator.inl - ../memberload.h - ../method.hpp - ../method.inl - ../methodimpl.h - ../methodtable.h - ../methodtable.inl - ../methodtablebuilder.h - ../methodtablebuilder.inl - ../mlinfo.h - ../olevariant.h - ../pefile.h - ../pefile.inl - ../peimage.h - ../peimage.inl - ../peimagelayout.h - ../peimagelayout.inl - ../pendingload.h - ../precode.h - ../sigformat.h - ../siginfo.hpp - ../simplerwlock.hpp - ../spinlock.h - ../stackingallocator.h - ../stubcache.h - ../stubgen.h - ../stublink.h - ../stublink.inl - ../tailcallhelp.h - ../typectxt.h - ../typedesc.h - ../typedesc.inl - ../typeequivalencehash.hpp - ../typehandle.h - ../typehandle.inl - ../typehash.h - ../typeparse.h - ../typestring.h - ../util.hpp - ../vars.hpp - ../versionresilienthashcode.h - ../zapsig.h -) - -if(FEATURE_READYTORUN) - list(APPEND VM_CROSSGEN_SOURCES - ../readytoruninfo.cpp - ) - list(APPEND VM_CROSSGEN_HEADERS - ../readytoruninfo.h - ) -endif(FEATURE_READYTORUN) - -include_directories(BEFORE ..) -include_directories(${CLR_DIR}/gc) -include_directories(../${ARCH_SOURCES_DIR}) - -if(CLR_CMAKE_TARGET_ARCH_AMD64) - list(APPEND VM_CROSSGEN_SOURCES - ../${ARCH_SOURCES_DIR}/stublinkeramd64.cpp - ) - list(APPEND VM_CROSSGEN_HEADERS - ../${ARCH_SOURCES_DIR}/stublinkeramd64.h - ) -elseif(CLR_CMAKE_TARGET_ARCH_I386) - list(APPEND VM_CROSSGEN_SOURCES - ../${ARCH_SOURCES_DIR}/stublinkerx86.cpp - ../gcdecode.cpp - ) - list(APPEND VM_CROSSGEN_HEADERS - ../${ARCH_SOURCES_DIR}/stublinkerx86.h - ) -elseif(CLR_CMAKE_TARGET_ARCH_ARM) - list(APPEND VM_CROSSGEN_SOURCES - ../${ARCH_SOURCES_DIR}/stubs.cpp - ) -elseif(CLR_CMAKE_TARGET_ARCH_ARM64) - list(APPEND VM_CROSSGEN_SOURCES - ../${ARCH_SOURCES_DIR}/stubs.cpp - ) -elseif(CLR_CMAKE_TARGET_ARCH_S390X) - list(APPEND VM_CROSSGEN_SOURCES - # Not supported as VM target - ) -else() - clr_unknown_arch() -endif() - -if (CLR_CMAKE_TARGET_WIN32) - - # COM interop scenarios - list(APPEND VM_CROSSGEN_SOURCES - ../classcompat.cpp - ../comtoclrcall.cpp - ../clrtocomcall.cpp - ../runtimecallablewrapper.cpp - ) - list(APPEND VM_CROSSGEN_HEADERS - ../classcompat.h - ../clrtocomcall.h - ../comtoclrcall.h - ../runtimecallablewrapper.h - ) - - list(APPEND VM_CROSSGEN_SOURCES ${VM_CROSSGEN_HEADERS}) -endif (CLR_CMAKE_TARGET_WIN32) - -if (CLR_CMAKE_HOST_LINUX) - list(APPEND VM_CROSSGEN_SOURCES - ../perfmap.cpp - ../perfinfo.cpp - ) -endif (CLR_CMAKE_HOST_LINUX) - -add_library_clr(cee_crossgen ${VM_CROSSGEN_SOURCES}) -add_dependencies(cee_crossgen eventing_headers) -set_target_properties(cee_crossgen PROPERTIES CROSSGEN_COMPONENT TRUE) -target_precompile_headers(cee_crossgen PRIVATE [["common.h"]]) -if (MSVC) - # corelib.cpp does not compile with precompiled header file - set_source_files_properties(../corelib.cpp PROPERTIES COMPILE_FLAGS "/Y-") -endif() - -add_library_clr(corelib_crossgen ../corelib.cpp) -add_dependencies(corelib_crossgen eventing_headers) -target_compile_definitions(corelib_crossgen - PRIVATE - EnC_SUPPORTED - FEATURE_EVENT_TRACE - FEATURE_MULTICOREJIT - CROSSGEN_CORELIB) - -set_target_properties(corelib_crossgen PROPERTIES CROSSGEN_COMPONENT TRUE) - From eb57372f7eb239c301ddbff0b1314e5a8f7d66ae Mon Sep 17 00:00:00 2001 From: Fan Yang <52458914+fanyang-mono@users.noreply.github.com> Date: Mon, 21 Jun 2021 21:58:30 -0400 Subject: [PATCH 050/107] [Test] Move leakwheel to Pri1 (#54522) * Move to Pri1 * Remove leakwheel from issues.targets file for mono --- src/tests/GC/Scenarios/LeakWheel/leakwheel.csproj | 1 + src/tests/issues.targets | 6 ------ 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/tests/GC/Scenarios/LeakWheel/leakwheel.csproj b/src/tests/GC/Scenarios/LeakWheel/leakwheel.csproj index d0dca8486ab8a..ceda84368bf25 100644 --- a/src/tests/GC/Scenarios/LeakWheel/leakwheel.csproj +++ b/src/tests/GC/Scenarios/LeakWheel/leakwheel.csproj @@ -2,6 +2,7 @@ Exe true + 1 diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 46132c96a341b..25b94c7c75cb3 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -1696,9 +1696,6 @@ https://github.com/dotnet/runtime/issues/54395 - - needs triage - https://github.com/dotnet/runtime/issues/54396 @@ -3149,9 +3146,6 @@ https://github.com/dotnet/runtime/issues/53353 - - long running test https://github.com/dotnet/runtime/issues/53386 - From 8a20ae03566e3aabb0c95d2bb206a9ee780db4fd Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Mon, 21 Jun 2021 23:44:16 -0700 Subject: [PATCH 051/107] Convert some COM object checking functions to managed code (#54471) * Convert COM object checking to managed code * Convert IsComWrapperClass to a managed "can cast to" implementation. * Add testing for updates. --- .../InteropServices/Marshal.CoreCLR.cs | 11 ++++++-- .../src/System/RuntimeHandles.cs | 13 +++++++-- src/coreclr/vm/ecalllist.h | 2 -- src/coreclr/vm/interoputil.cpp | 19 ------------- src/coreclr/vm/interoputil.h | 4 --- src/coreclr/vm/marshalnative.cpp | 24 ---------------- src/coreclr/vm/marshalnative.h | 5 ---- src/coreclr/vm/runtimehandles.cpp | 28 ------------------- src/coreclr/vm/runtimehandles.h | 1 - .../COM/NETClients/Aggregation/Program.cs | 6 ++++ .../NETClients/ConsumeNETServer/Program.cs | 3 ++ 11 files changed, 29 insertions(+), 87 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs index 688e4f83908c7..e064c36746d51 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs @@ -464,8 +464,15 @@ public static IntPtr CreateAggregatedObject(IntPtr pOuter, T o) where T : not /// /// Checks if the object is classic COM component. /// - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern bool IsComObject(object o); + public static bool IsComObject(object o) + { + if (o is null) + { + throw new ArgumentNullException(nameof(o)); + } + + return o is __ComObject; + } /// /// Release the COM component and if the reference hits 0 zombie this object. diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs index 75aff556de7c3..dd17a2fa74418 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -460,8 +460,17 @@ internal RuntimeMethodHandleInternal GetInterfaceMethodImplementation(RuntimeTyp return GetInterfaceMethodImplementation(new QCallTypeHandle(ref nativeHandle), new QCallTypeHandle(ref nativeInterfaceHandle), interfaceMethodHandle); } - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool IsComObject(RuntimeType type, bool isGenericCOM); + internal static bool IsComObject(RuntimeType type, bool isGenericCOM) + { +#if FEATURE_COMINTEROP + if (isGenericCOM) + return type == typeof(__ComObject); + + return RuntimeTypeHandle.CanCastTo(type, (RuntimeType)typeof(__ComObject)); +#else + return false; +#endif + } [MethodImpl(MethodImplOptions.InternalCall)] internal static extern bool IsInterface(RuntimeType type); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 48a2d7d322e57..1822422f5961c 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -209,7 +209,6 @@ FCFuncStart(gCOMTypeHandleFuncs) FCFuncElement("GetNumVirtualsAndStaticVirtuals", RuntimeTypeHandle::GetNumVirtualsAndStaticVirtuals) QCFuncElement("VerifyInterfaceIsImplemented", RuntimeTypeHandle::VerifyInterfaceIsImplemented) QCFuncElement("GetInterfaceMethodImplementation", RuntimeTypeHandle::GetInterfaceMethodImplementation) - FCFuncElement("IsComObject", RuntimeTypeHandle::IsComObject) FCFuncElement("IsValueType", RuntimeTypeHandle::IsValueType) FCFuncElement("IsInterface", RuntimeTypeHandle::IsInterface) FCFuncElement("IsByRefLike", RuntimeTypeHandle::IsByRefLike) @@ -768,7 +767,6 @@ FCFuncStart(gInteropMarshalFuncs) #ifdef FEATURE_COMINTEROP FCFuncElement("GetHRForException", MarshalNative::GetHRForException) - FCFuncElement("IsComObject", MarshalNative::IsComObject) FCFuncElement("GetObjectForIUnknownNative", MarshalNative::GetObjectForIUnknownNative) FCFuncElement("GetUniqueObjectForIUnknownNative", MarshalNative::GetUniqueObjectForIUnknownNative) FCFuncElement("GetNativeVariantForObjectNative", MarshalNative::GetNativeVariantForObjectNative) diff --git a/src/coreclr/vm/interoputil.cpp b/src/coreclr/vm/interoputil.cpp index e3797b13d508c..2b6697887aa97 100644 --- a/src/coreclr/vm/interoputil.cpp +++ b/src/coreclr/vm/interoputil.cpp @@ -820,25 +820,6 @@ BOOL CanCastComObject(OBJECTREF obj, MethodTable * pTargetMT) } } -// Returns TRUE iff the argument represents the "__ComObject" type or -// any type derived from it (i.e. typelib-imported RCWs). -BOOL IsComWrapperClass(TypeHandle type) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - MethodTable* pMT = type.GetMethodTable(); - if (pMT == NULL) - return FALSE; - - return pMT->IsComObjectType(); -} - // Returns TRUE iff the argument represents the "__ComObject" type. BOOL IsComObjectClass(TypeHandle type) { diff --git a/src/coreclr/vm/interoputil.h b/src/coreclr/vm/interoputil.h index c7209467b4a79..b99764688dd53 100644 --- a/src/coreclr/vm/interoputil.h +++ b/src/coreclr/vm/interoputil.h @@ -83,10 +83,6 @@ ULONG SafeReleasePreemp(IUnknown* pUnk, RCW* pRCW = NULL); // Determines if a COM object can be cast to the specified type. BOOL CanCastComObject(OBJECTREF obj, MethodTable * pTargetMT); -// includes Types which hold a "ComObject" class -// and types which are imported through typelib -BOOL IsComWrapperClass(TypeHandle type); - // includes Type which hold a "__ComObject" class BOOL IsComObjectClass(TypeHandle type); diff --git a/src/coreclr/vm/marshalnative.cpp b/src/coreclr/vm/marshalnative.cpp index 6d1c38b9d3353..b28f34aa26600 100644 --- a/src/coreclr/vm/marshalnative.cpp +++ b/src/coreclr/vm/marshalnative.cpp @@ -946,30 +946,6 @@ FCIMPL0(FC_BOOL_RET, MarshalNative::AreComObjectsAvailableForCleanup) } FCIMPLEND -//==================================================================== -// check if the object is classic COM component -//==================================================================== -FCIMPL1(FC_BOOL_RET, MarshalNative::IsComObject, Object* objUNSAFE) -{ - FCALL_CONTRACT; - - BOOL retVal = FALSE; - OBJECTREF obj = (OBJECTREF) objUNSAFE; - HELPER_METHOD_FRAME_BEGIN_RET_1(obj); - - if(!obj) - COMPlusThrowArgumentNull(W("o")); - - MethodTable* pMT = obj->GetMethodTable(); - PREFIX_ASSUME(pMT != NULL); - retVal = pMT->IsComObjectType(); - - HELPER_METHOD_FRAME_END(); - FC_RETURN_BOOL(retVal); -} -FCIMPLEND - - //==================================================================== // free the COM component and zombie this object if the ref count hits 0 // further usage of this Object might throw an exception, diff --git a/src/coreclr/vm/marshalnative.h b/src/coreclr/vm/marshalnative.h index 790c7316d32b5..8a3615294ffc6 100644 --- a/src/coreclr/vm/marshalnative.h +++ b/src/coreclr/vm/marshalnative.h @@ -103,11 +103,6 @@ class MarshalNative //==================================================================== static FCDECL2(IUnknown*, CreateAggregatedObjectNative, IUnknown* pOuter, Object* refObjUNSAFE); - //==================================================================== - // check if the object is classic COM component - //==================================================================== - static FCDECL1(FC_BOOL_RET, IsComObject, Object* objUNSAFE); - //==================================================================== // free the COM component and zombie this object // further usage of this Object might throw an exception, diff --git a/src/coreclr/vm/runtimehandles.cpp b/src/coreclr/vm/runtimehandles.cpp index a8851f307edc5..d3c21535cc4a5 100644 --- a/src/coreclr/vm/runtimehandles.cpp +++ b/src/coreclr/vm/runtimehandles.cpp @@ -1033,34 +1033,6 @@ RuntimeTypeHandle::IsVisible( return fIsExternallyVisible; } // RuntimeTypeHandle::IsVisible -FCIMPL2(FC_BOOL_RET, RuntimeTypeHandle::IsComObject, ReflectClassBaseObject *pTypeUNSAFE, CLR_BOOL isGenericCOM) { - CONTRACTL { - FCALL_CHECK; - } - CONTRACTL_END; - - BOOL ret = FALSE; - - REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE); - - if (refType == NULL) - FCThrowRes(kArgumentNullException, W("Arg_InvalidHandle")); - - TypeHandle typeHandle = refType->GetType(); - - HELPER_METHOD_FRAME_BEGIN_RET_1(refType); - { - if (isGenericCOM) - ret = IsComObjectClass(typeHandle); - else - ret = IsComWrapperClass(typeHandle); - } - HELPER_METHOD_FRAME_END(); - - FC_RETURN_BOOL(ret); -} -FCIMPLEND - FCIMPL1(LPCUTF8, RuntimeTypeHandle::GetUtf8Name, ReflectClassBaseObject* pTypeUNSAFE) { CONTRACTL { FCALL_CHECK; diff --git a/src/coreclr/vm/runtimehandles.h b/src/coreclr/vm/runtimehandles.h index d40b45458af1f..33645adc3eca7 100644 --- a/src/coreclr/vm/runtimehandles.h +++ b/src/coreclr/vm/runtimehandles.h @@ -191,7 +191,6 @@ class RuntimeTypeHandle { static BOOL QCALLTYPE IsVisible(QCall::TypeHandle pTypeHandle); - static FCDECL2(FC_BOOL_RET, IsComObject, ReflectClassBaseObject *pType, CLR_BOOL isGenericCOM); static FCDECL2(FC_BOOL_RET, CanCastTo, ReflectClassBaseObject *pType, ReflectClassBaseObject *pTarget); static FCDECL2(FC_BOOL_RET, IsInstanceOfType, ReflectClassBaseObject *pType, Object *object); diff --git a/src/tests/Interop/COM/NETClients/Aggregation/Program.cs b/src/tests/Interop/COM/NETClients/Aggregation/Program.cs index ee1984f5842cc..2072b41a01184 100644 --- a/src/tests/Interop/COM/NETClients/Aggregation/Program.cs +++ b/src/tests/Interop/COM/NETClients/Aggregation/Program.cs @@ -21,6 +21,12 @@ static void ValidateNativeOuter() var managedInner = new ManagedInner(); var nativeOuter = (AggregationTesting)managedInner; + Assert.IsTrue(typeof(ManagedInner).IsCOMObject); + Assert.IsTrue(typeof(AggregationTestingClass).IsCOMObject); + Assert.IsFalse(typeof(AggregationTesting).IsCOMObject); + Assert.IsTrue(Marshal.IsComObject(managedInner)); + Assert.IsTrue(Marshal.IsComObject(nativeOuter)); + Assert.IsTrue(nativeOuter.IsAggregated()); Assert.IsTrue(nativeOuter.AreAggregated(managedInner, nativeOuter)); Assert.IsFalse(nativeOuter.AreAggregated(nativeOuter, new object())); diff --git a/src/tests/Interop/COM/NETClients/ConsumeNETServer/Program.cs b/src/tests/Interop/COM/NETClients/ConsumeNETServer/Program.cs index a78274eb9c8aa..1bade41b255d9 100644 --- a/src/tests/Interop/COM/NETClients/ConsumeNETServer/Program.cs +++ b/src/tests/Interop/COM/NETClients/ConsumeNETServer/Program.cs @@ -24,6 +24,9 @@ static void Validate_Activation() // The CoClass should be the activated type, _not_ the activation interface. Assert.AreEqual(test.GetType(), typeof(CoClass.ConsumeNETServerTestingClass)); + Assert.IsTrue(typeof(CoClass.ConsumeNETServerTestingClass).IsCOMObject); + Assert.IsFalse(typeof(CoClass.ConsumeNETServerTesting).IsCOMObject); + Assert.IsTrue(Marshal.IsComObject(test)); } static void Validate_CCW_Wasnt_Unwrapped() From 849033aea0ac5746e9030961c3eadd51149bcf90 Mon Sep 17 00:00:00 2001 From: Michael Simons Date: Tue, 22 Jun 2021 03:14:07 -0500 Subject: [PATCH 052/107] ArPow stage 1: local source-build infrastructure (#53294) * Initial arcade-powered source-build infra * Add patches, fixup from 5.0 * Rename patches to match 6.0-p1 work * Add source-build specific build script * Incorporate build-source-build.sh, into eng/ * Run inner build script, through coreclr * Initial source-build args based on live build (not props file) * Cleanup: add RID comments, rm empty/absolute args * Fix subsets * Disable sourcelink in env, not args * Fix CI NZEC * Revert "Use same code to DetectCiphersuiteConfiguration for portable and non-portable builds" This reverts commit 464010d9d0241bbdcbfbda25b32e78991ddf6093. * Fix portability * Fix AllJits build * Fix missing crossgen2 for non-portable RID * Create supplemental intermediate nupkgs * Tweak category names * Use centralized supplemental nupkg infra * Add additional 6.0 patches * Patch updates after merging in main * SourceBuild.props cleanup * Fix issue with incorrect patch merge * Patch updates * Edit clr.tools patch * patch updates * Revert patch integration * Patch updates * Edits per code review feedback * ILAsm workaround * patch updates * Move logic to set ILAsmToolPath for source-build * Update eng/SourceBuild.props Co-authored-by: Viktor Hofer * Remove libraries specific patches * Patch updates necessary with latest main merge * Add back libraries-packages.proj harvesting patch * Refactor intermediate package split to be chunkier * Integrate patch 0017 * Subsets update per code review * Remove obsolete patch * Removed patches that were integrated into main * Remove two additional patches * Remove remaining patches Co-authored-by: Davis Goodin Co-authored-by: dseefeld Co-authored-by: Viktor Hofer --- Directory.Build.props | 1 + eng/SourceBuild.props | 73 +++++++++++++++++++++++++++ eng/SourceBuildPrebuiltBaseline.xml | 5 ++ eng/Subsets.props | 21 ++++---- eng/Version.Details.xml | 1 + eng/packaging.props | 4 -- eng/restore/harvestPackages.targets | 10 ++++ src/libraries/Directory.Build.props | 1 - src/libraries/libraries-packages.proj | 16 ++---- 9 files changed, 105 insertions(+), 27 deletions(-) create mode 100644 eng/SourceBuild.props create mode 100644 eng/SourceBuildPrebuiltBaseline.xml diff --git a/Directory.Build.props b/Directory.Build.props index d1b03d7ef8de0..e2c0de947f336 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -83,6 +83,7 @@ $([MSBuild]::NormalizePath('$(WasmBuildTasksDir)', 'WasmBuildTasks.dll')) $([MSBuild]::NormalizePath('$(MonoAOTCompilerDir)', 'MonoAOTCompiler.dll')) $([MSBuild]::NormalizePath('$(RuntimeConfigParserDir)', 'RuntimeConfigParser.dll')) + $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'coreclr', '$(TargetOS).$(TargetArchitecture).$(Configuration)')) diff --git a/eng/SourceBuild.props b/eng/SourceBuild.props new file mode 100644 index 0000000000000..7dfc80d41fd14 --- /dev/null +++ b/eng/SourceBuild.props @@ -0,0 +1,73 @@ + + + + runtime + + + + + ./build.sh + + true + false + + + $([System.Runtime.InteropServices.RuntimeInformation]::RuntimeIdentifier) + $(__DistroRid) + + + <_targetRidPlatformIndex>$(TargetRid.LastIndexOfAny("-")) + $(TargetRid.Substring(0, $(_targetRidPlatformIndex))) + $(TargetRid.Substring($(_targetRidPlatformIndex)).TrimStart('-')) + + minimal + + + + + + + + + + + + + $(InnerBuildArgs) --arch $(TargetRidPlatform) + $(InnerBuildArgs) --configuration $(Configuration) + $(InnerBuildArgs) --ci + $(InnerBuildArgs) --allconfigurations + $(InnerBuildArgs) --verbosity $(LogVerbosity) + $(InnerBuildArgs) --nodereuse false + $(InnerBuildArgs) --warnAsError false + $(InnerBuildArgs) --cmakeargs -DCLR_CMAKE_USE_SYSTEM_LIBUNWIND=TRUE + $(InnerBuildArgs) /p:MicrosoftNetFrameworkReferenceAssembliesVersion=1.0.0 + $(InnerBuildArgs) /p:ContinuousIntegrationBuild=true + $(InnerBuildArgs) /p:PackageRid=$(TargetRid) + $(InnerBuildArgs) /p:NoPgoOptimize=true + $(InnerBuildArgs) /p:KeepNativeSymbols=true + $(InnerBuildArgs) /p:RuntimeOS=$(TargetRidWithoutPlatform) + $(InnerBuildArgs) /p:PortableBuild=$(SourceBuildPortable) + $(InnerBuildArgs) /p:BuildDebPackage=false + + + + + + + + + + + + + diff --git a/eng/SourceBuildPrebuiltBaseline.xml b/eng/SourceBuildPrebuiltBaseline.xml new file mode 100644 index 0000000000000..c1b6dfbf05381 --- /dev/null +++ b/eng/SourceBuildPrebuiltBaseline.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/eng/Subsets.props b/eng/Subsets.props index bf6b25bffc731..105b34254c6c2 100644 --- a/eng/Subsets.props +++ b/eng/Subsets.props @@ -25,8 +25,8 @@ clr+mono+libs+host+packs mono+libs+packs - - clr+libs+host+packs + + clr+libs+host+packs @@ -49,18 +49,21 @@ $(DefaultMonoSubsets)mono.wasmruntime+ $(DefaultMonoSubsets)mono.aotcross+ $(DefaultMonoSubsets)mono.runtime+mono.corelib+mono.packages - + libs.native+ - $(DefaultLibrariesSubsets)libs.ref+libs.src+libs.pretest+libs.packages + $(DefaultLibrariesSubsets)libs.ref+libs.src+libs.packages + $(DefaultLibrariesSubsets)+libs.pretest - host.native+host.pkg+host.tools+host.tests + host.native+host.tools + $(DefaultHostSubsets)+host.pkg+host.tests host.native packs.product $(DefaultPacksSubsets)+packs.tests + $(DefaultPacksSubsets)+packs.installers @@ -204,12 +207,12 @@ + $(CoreClrProjectRoot)tools\r2rtest\R2RTest.csproj" Category="clr" Condition="'$(DotNetBuildFromSource)' != 'true'"/> + + Test="true" Category="clr" Condition="'$(__DistroRid)' != 'linux-musl-x64' and '$(DotNetBuildFromSource)' != 'true'"/> @@ -221,7 +224,7 @@ - + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index f1ef354b76c66..4177e280edf32 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -9,6 +9,7 @@ https://github.com/dotnet/arcade 4a2b475948d498b89fedef7cf890883f49bc1ea3 + https://github.com/dotnet/arcade diff --git a/eng/packaging.props b/eng/packaging.props index 87cecef871a69..534d3ddcf321b 100644 --- a/eng/packaging.props +++ b/eng/packaging.props @@ -16,10 +16,6 @@ - - false - - true false diff --git a/eng/restore/harvestPackages.targets b/eng/restore/harvestPackages.targets index a7fc8aa34655f..07eb5a91a7a8e 100644 --- a/eng/restore/harvestPackages.targets +++ b/eng/restore/harvestPackages.targets @@ -1,4 +1,14 @@  + + + $(NuGetPackageRoot)microsoft.dotnet.build.tasks.packaging\$(MicrosoftDotNetBuildTasksPackagingVersion)\tools\ + $(PackagingTaskAssembly)netcoreapp3.1\ + $(PackagingTaskAssembly)net472\ + + $(PackagingTaskAssembly)net5.0\ + $(PackagingTaskAssembly)Microsoft.DotNet.Build.Tasks.Packaging.dll + + diff --git a/src/libraries/Directory.Build.props b/src/libraries/Directory.Build.props index b06fc950644f2..0078c1331d34d 100644 --- a/src/libraries/Directory.Build.props +++ b/src/libraries/Directory.Build.props @@ -28,7 +28,6 @@ net461;net462;net47;net471;net472 - $(AdditionalBuildTargetFrameworks);netstandard2.0 diff --git a/src/libraries/libraries-packages.proj b/src/libraries/libraries-packages.proj index 2f4d0949db3ef..383dd088e127c 100644 --- a/src/libraries/libraries-packages.proj +++ b/src/libraries/libraries-packages.proj @@ -1,20 +1,11 @@ - - $(AdditionalBuildTargetFrameworks);package-$(Configuration) - - - - $(NuGetPackageRoot)microsoft.dotnet.build.tasks.packaging\$(MicrosoftDotNetBuildTasksPackagingVersion)\tools\ - $(PackagingTaskAssembly)netcoreapp3.1\ - $(PackagingTaskAssembly)net472\ - $(PackagingTaskAssembly)Microsoft.DotNet.Build.Tasks.Packaging.dll - - + + @@ -31,7 +22,6 @@ ones that might do this. After we ship a stable set of packages this target should be ran and the changes to the package index should be commited to the repo. --> - From 04ad80aff37e2ef5eac0cc6d2a895ccf0f9d65a3 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 22 Jun 2021 09:53:13 +0000 Subject: [PATCH 053/107] [main] Update dependencies from 9 repositories (#54218) [main] Update dependencies from 9 repositories - Merge branch 'main' into darc-main-b0a81754-f267-416d-a8e2-cf56f8c1ee3e - PInvoke warnings fixes for OOB assemblies - Update testPackages.proj - Update testPackages.proj - Update dependencies from https://github.com/dotnet/arcade build 20210621.1 - Merge branch 'main' into darc-main-b0a81754-f267-416d-a8e2-cf56f8c1ee3e Conflicts: eng/Version.Details.xml eng/Versions.props - Fix xml - Update dependencies from https://github.com/dotnet/xharness build 20210622.2 --- .config/dotnet-tools.json | 2 +- eng/Version.Details.xml | 203 +++++++++--------- eng/Versions.props | 88 ++++---- eng/common/generate-locproject.ps1 | 13 +- .../templates/job/source-index-stage1.yml | 4 + global.json | 10 +- .../src/ILLink/ILLink.Suppressions.xml | 2 +- .../src/ILLink/ILLink.Suppressions.xml | 20 +- .../src/ILLink/ILLink.Suppressions.xml | 2 +- .../src/ILLink/ILLink.Suppressions.xml | 2 +- .../src/ILLink/ILLink.Suppressions.xml | 26 +-- src/libraries/pkg/test/testPackages.proj | 2 +- 12 files changed, 198 insertions(+), 176 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 02e0543e98b8e..6428121a0fec7 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "microsoft.dotnet.xharness.cli": { - "version": "1.0.0-prerelease.21314.1", + "version": "1.0.0-prerelease.21322.2", "commands": [ "xharness" ] diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 4177e280edf32..e8995dca95d92 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,222 +1,221 @@ - + https://github.com/dotnet/icu - d7db669b70f4dd67ec001c192f9809c218cab88b + 59588c1257a842089d0b7df3bad1cdd69ac720e1 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 - + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 https://github.com/microsoft/vstest 140434f7109d357d0158ade9e5164a4861513965 - + https://github.com/dotnet/runtime-assets - 0612b036e67746930105231b605c4df9ac6ed47e + 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/runtime-assets - 0612b036e67746930105231b605c4df9ac6ed47e + 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/runtime-assets - 0612b036e67746930105231b605c4df9ac6ed47e + 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/runtime-assets - 0612b036e67746930105231b605c4df9ac6ed47e + 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/runtime-assets - 0612b036e67746930105231b605c4df9ac6ed47e + 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/runtime-assets - 0612b036e67746930105231b605c4df9ac6ed47e + 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/runtime-assets - 0612b036e67746930105231b605c4df9ac6ed47e + 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/runtime-assets - 0612b036e67746930105231b605c4df9ac6ed47e + 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/runtime-assets - 0612b036e67746930105231b605c4df9ac6ed47e + 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/llvm-project - a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 + 4f0293e0a254a2f014643ecbe973b81f26c87fd4 - + https://github.com/dotnet/llvm-project - a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 + 4f0293e0a254a2f014643ecbe973b81f26c87fd4 - + https://github.com/dotnet/llvm-project - a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 + 4f0293e0a254a2f014643ecbe973b81f26c87fd4 - + https://github.com/dotnet/llvm-project - a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 + 4f0293e0a254a2f014643ecbe973b81f26c87fd4 - + https://github.com/dotnet/llvm-project - a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 + 4f0293e0a254a2f014643ecbe973b81f26c87fd4 - + https://github.com/dotnet/llvm-project - a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 + 4f0293e0a254a2f014643ecbe973b81f26c87fd4 - + https://github.com/dotnet/llvm-project - a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 + 4f0293e0a254a2f014643ecbe973b81f26c87fd4 - + https://github.com/dotnet/llvm-project - a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 + 4f0293e0a254a2f014643ecbe973b81f26c87fd4 https://github.com/dotnet/runtime 38017c3935de95d0335bac04f4901ddfc2718656 - + https://github.com/dotnet/runtime - af5c238556e204583b129cc8f5c7338f84dc2c40 + f891033db5b8ebf651176a3dcc3bec74a217f85e - + https://github.com/dotnet/runtime - af5c238556e204583b129cc8f5c7338f84dc2c40 + f891033db5b8ebf651176a3dcc3bec74a217f85e - + https://github.com/dotnet/runtime - af5c238556e204583b129cc8f5c7338f84dc2c40 + f891033db5b8ebf651176a3dcc3bec74a217f85e - + https://github.com/dotnet/runtime - af5c238556e204583b129cc8f5c7338f84dc2c40 + f891033db5b8ebf651176a3dcc3bec74a217f85e - + https://github.com/dotnet/runtime - af5c238556e204583b129cc8f5c7338f84dc2c40 + f891033db5b8ebf651176a3dcc3bec74a217f85e - + https://github.com/dotnet/runtime - af5c238556e204583b129cc8f5c7338f84dc2c40 + f891033db5b8ebf651176a3dcc3bec74a217f85e - + https://github.com/dotnet/runtime - af5c238556e204583b129cc8f5c7338f84dc2c40 + f891033db5b8ebf651176a3dcc3bec74a217f85e - + https://github.com/mono/linker - caeaf2a3fb3f636805fdd4881df4f9a539fff8f6 + c739a81ba553b00df1cb2f5b9974deae996b757a - + https://github.com/dotnet/xharness - d6f8a4ad30908fb210390380eae97264e4fbe8ce + 0bb4a23b1b686e8fefde9d4c860b26a8fafc303e - + https://github.com/dotnet/xharness - d6f8a4ad30908fb210390380eae97264e4fbe8ce + 0bb4a23b1b686e8fefde9d4c860b26a8fafc303e - + https://github.com/dotnet/arcade - 85a65ea1fca1d0867f699fed44d191358270bf6a + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 4e5bea15eb5a9c8cf9142195b1c9c78437a5b27f + f291c7f87a563f29ff2a9af7378495769d97389c - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 4e5bea15eb5a9c8cf9142195b1c9c78437a5b27f + f291c7f87a563f29ff2a9af7378495769d97389c - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 4e5bea15eb5a9c8cf9142195b1c9c78437a5b27f + f291c7f87a563f29ff2a9af7378495769d97389c - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 4e5bea15eb5a9c8cf9142195b1c9c78437a5b27f + f291c7f87a563f29ff2a9af7378495769d97389c https://github.com/dotnet/emsdk defa37b05c734e025292c5747664e970cd2ac444 - + https://github.com/dotnet/hotreload-utils - 25b814e010cd4796cedfbcce72a274c26928f496 + 6adb8ac00a59fe409f232b8b32758aa7d10b4d1d - https://github.com/dotnet/runtime-assets + https://github.com/dotnet/runtime-assets 8d7b898b96cbdb868cac343e938173105287ed9e diff --git a/eng/Versions.props b/eng/Versions.props index b62b9b4854cfc..3bc9edaa1b14c 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -49,28 +49,28 @@ 3.10.0-2.final 3.10.0-2.final - 6.0.0-beta.21311.3 - 6.0.0-beta.21311.3 - 6.0.0-beta.21311.3 - 6.0.0-beta.21311.3 - 6.0.0-beta.21311.3 - 6.0.0-beta.21311.3 - 2.5.1-beta.21311.3 - 6.0.0-beta.21311.3 - 6.0.0-beta.21311.3 - 6.0.0-beta.21311.3 - 6.0.0-beta.21311.3 - 6.0.0-beta.21311.3 - 6.0.0-beta.21311.3 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 + 2.5.1-beta.21321.1 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 5.9.0-preview.2 6.0.0-alpha.1.20612.4 - 6.0.0-preview.6.21314.1 - 6.0.0-preview.6.21314.1 + 6.0.0-preview.7.21321.2 + 6.0.0-preview.7.21321.2 3.1.0 - 6.0.0-preview.6.21314.1 + 6.0.0-preview.7.21321.2 5.0.0 4.3.0 @@ -104,27 +104,27 @@ 5.0.0 5.0.0 4.8.1 - 6.0.0-preview.6.21314.1 - 6.0.0-preview.6.21314.1 + 6.0.0-preview.7.21321.2 + 6.0.0-preview.7.21321.2 4.5.4 4.5.0 - 6.0.0-preview.6.21314.1 + 6.0.0-preview.7.21321.2 6.0.0-beta.21314.1 6.0.0-beta.21307.1 - 6.0.0-beta.21307.1 - 6.0.0-beta.21307.1 - 6.0.0-beta.21307.1 - 6.0.0-beta.21307.1 - 6.0.0-beta.21307.1 - 6.0.0-beta.21307.1 - 6.0.0-beta.21307.1 - 6.0.0-beta.21307.1 + 6.0.0-beta.21314.1 + 6.0.0-beta.21314.1 + 6.0.0-beta.21314.1 + 6.0.0-beta.21314.1 + 6.0.0-beta.21314.1 + 6.0.0-beta.21314.1 + 6.0.0-beta.21314.1 + 6.0.0-beta.21314.1 - 1.0.0-prerelease.21313.4 - 1.0.0-prerelease.21313.4 - 1.0.0-prerelease.21313.4 - 1.0.0-prerelease.21313.4 + 1.0.0-prerelease.21320.4 + 1.0.0-prerelease.21320.4 + 1.0.0-prerelease.21320.4 + 1.0.0-prerelease.21320.4 16.9.0-beta1.21055.5 2.0.0-beta1.20253.1 @@ -148,9 +148,9 @@ 1.0.1-prerelease-00006 16.9.0-preview-20201201-01 - 1.0.0-prerelease.21314.1 - 1.0.0-prerelease.21314.1 - 1.0.1-alpha.0.21311.1 + 1.0.0-prerelease.21322.2 + 1.0.0-prerelease.21322.2 + 1.0.1-alpha.0.21314.1 2.4.1 2.4.2 1.3.0 @@ -161,19 +161,19 @@ 5.0.0-preview-20201009.2 - 6.0.100-preview.6.21310.3 + 6.0.100-preview.6.21317.4 $(MicrosoftNETILLinkTasksVersion) - 6.0.0-preview.6.21307.1 + 6.0.0-preview.7.21315.3 - 11.1.0-alpha.1.21308.1 - 11.1.0-alpha.1.21308.1 - 11.1.0-alpha.1.21308.1 - 11.1.0-alpha.1.21308.1 - 11.1.0-alpha.1.21308.1 - 11.1.0-alpha.1.21308.1 - 11.1.0-alpha.1.21308.1 - 11.1.0-alpha.1.21308.1 + 11.1.0-alpha.1.21314.1 + 11.1.0-alpha.1.21314.1 + 11.1.0-alpha.1.21314.1 + 11.1.0-alpha.1.21314.1 + 11.1.0-alpha.1.21314.1 + 11.1.0-alpha.1.21314.1 + 11.1.0-alpha.1.21314.1 + 11.1.0-alpha.1.21314.1 6.0.0-preview.6.21275.1 $(MicrosoftNETRuntimeEmscripten2021Nodewinx64Version) diff --git a/eng/common/generate-locproject.ps1 b/eng/common/generate-locproject.ps1 index de348a2e225c0..25e97ac00772d 100644 --- a/eng/common/generate-locproject.ps1 +++ b/eng/common/generate-locproject.ps1 @@ -25,8 +25,15 @@ Push-Location "$SourcesDirectory" # push location for Resolve-Path -Relative to # Template files $jsonFiles = @() -$jsonFiles += Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "\.template\.config\\localize\\en\..+\.json" } # .NET templating pattern -$jsonFiles += Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "en\\strings\.json" } # current winforms pattern +$jsonTemplateFiles = Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "\.template\.config\\localize\\.+\.en\.json" } # .NET templating pattern +$jsonTemplateFiles | ForEach-Object { + $null = $_.Name -Match "(.+)\.[\w-]+\.json" # matches '[filename].[langcode].json + + $destinationFile = "$($_.Directory.FullName)\$($Matches.1).json" + $jsonFiles += Copy-Item "$($_.FullName)" -Destination $destinationFile -PassThru +} + +$jsonWinformsTemplateFiles = Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "en\\strings\.json" } # current winforms pattern $xlfFiles = @() @@ -44,7 +51,7 @@ $langXlfFiles | ForEach-Object { $xlfFiles += Copy-Item "$($_.FullName)" -Destination $destinationFile -PassThru } -$locFiles = $jsonFiles + $xlfFiles +$locFiles = $jsonFiles + $jsonWinformsTemplateFiles + $xlfFiles $locJson = @{ Projects = @( diff --git a/eng/common/templates/job/source-index-stage1.yml b/eng/common/templates/job/source-index-stage1.yml index 6e8aa9f7f218b..b58d42364b9e7 100644 --- a/eng/common/templates/job/source-index-stage1.yml +++ b/eng/common/templates/job/source-index-stage1.yml @@ -7,9 +7,13 @@ parameters: binlogPath: artifacts/log/Debug/Build.binlog pool: vmImage: vs2017-win2016 + condition: '' + dependsOn: '' jobs: - job: SourceIndexStage1 + dependsOn: ${{ parameters.dependsOn }} + condition: ${{ parameters.condition }} variables: - name: SourceIndexPackageVersion value: ${{ parameters.sourceIndexPackageVersion }} diff --git a/global.json b/global.json index df8f7ead63774..433797e811628 100644 --- a/global.json +++ b/global.json @@ -12,13 +12,13 @@ "python3": "3.7.1" }, "msbuild-sdks": { - "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21311.3", + "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21321.1", "Microsoft.DotNet.PackageValidation": "1.0.0-preview.6.21274.7", - "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21311.3", - "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21311.3", - "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21311.3", + "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21321.1", + "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21321.1", + "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21321.1", "Microsoft.Build.NoTargets": "3.0.4", "Microsoft.Build.Traversal": "3.0.23", - "Microsoft.NET.Sdk.IL": "6.0.0-preview.6.21314.1" + "Microsoft.NET.Sdk.IL": "6.0.0-preview.7.21321.2" } } diff --git a/src/libraries/System.Data.Odbc/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Data.Odbc/src/ILLink/ILLink.Suppressions.xml index 533fc1cd0ed04..0504df00e7659 100644 --- a/src/libraries/System.Data.Odbc/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Data.Odbc/src/ILLink/ILLink.Suppressions.xml @@ -5,7 +5,7 @@ ILLink IL2050 member - M:Interop.Odbc.SQLSetConnectAttrW(System.Data.Odbc.OdbcConnectionHandle,System.Data.Odbc.ODBC32.SQL_ATTR,System.Transactions.IDtcTransaction,System.Int32) + M:System.Data.Odbc.OdbcConnectionHandle.SetConnectionAttribute4(System.Data.Odbc.ODBC32.SQL_ATTR,System.Transactions.IDtcTransaction,System.Int32) ILLink diff --git a/src/libraries/System.Data.OleDb/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Data.OleDb/src/ILLink/ILLink.Suppressions.xml index 2e52a33c8a98e..5386e4bd294fc 100644 --- a/src/libraries/System.Data.OleDb/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Data.OleDb/src/ILLink/ILLink.Suppressions.xml @@ -5,7 +5,25 @@ ILLink IL2050 member - M:System.Data.Common.UnsafeNativeMethods.GetErrorInfo(System.Int32,System.Data.Common.UnsafeNativeMethods.IErrorInfo@) + M:System.Data.OleDb.DBPropSet.SetLastErrorInfo(System.Data.OleDb.OleDbHResult) + + + ILLink + IL2050 + member + M:System.Data.OleDb.OleDbConnection.ProcessResults(System.Data.OleDb.OleDbHResult,System.Data.OleDb.OleDbConnection,System.Object) + + + ILLink + IL2050 + member + M:System.Data.OleDb.OleDbDataAdapter.FillClose(System.Boolean,System.Object) + + + ILLink + IL2050 + member + M:System.Data.OleDb.OleDbDataAdapter.FillFromADODB(System.Object,System.Object,System.String,System.Boolean) ILLink diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.DirectoryServices.AccountManagement/src/ILLink/ILLink.Suppressions.xml index ae9db1185dd9c..7572cc7074afb 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/ILLink/ILLink.Suppressions.xml @@ -5,7 +5,7 @@ ILLink IL2050 member - M:System.DirectoryServices.AccountManagement.UnsafeNativeMethods.IntADsOpenObject(System.String,System.String,System.String,System.Int32,System.Guid@,System.Object@) + M:System.DirectoryServices.AccountManagement.UnsafeNativeMethods.ADsOpenObject(System.String,System.String,System.String,System.Int32,System.Guid@,System.Object@) ILLink diff --git a/src/libraries/System.DirectoryServices/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.DirectoryServices/src/ILLink/ILLink.Suppressions.xml index 0e87e736269c4..ca9681a8e8f63 100644 --- a/src/libraries/System.DirectoryServices/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.DirectoryServices/src/ILLink/ILLink.Suppressions.xml @@ -5,7 +5,7 @@ ILLink IL2050 member - M:System.DirectoryServices.Interop.UnsafeNativeMethods.IntADsOpenObject(System.String,System.String,System.String,System.Int32,System.Guid@,System.Object@) + M:System.DirectoryServices.Interop.UnsafeNativeMethods.ADsOpenObject(System.String,System.String,System.String,System.Int32,System.Guid@,System.Object@) ILLink diff --git a/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml index d5734aab1a2a9..3f367cccd2496 100644 --- a/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml @@ -5,67 +5,61 @@ ILLink IL2050 member - M:System.Drawing.Icon.OleCreatePictureIndirect(System.Drawing.Icon.PICTDESC,System.Guid@,System.Boolean) + M:System.Drawing.Bitmap.#ctor(System.IO.Stream,System.Boolean) ILLink IL2050 member - M:System.Drawing.SafeNativeMethods.Gdip.GdipCreateBitmapFromStream(Interop.Ole32.IStream,System.IntPtr@) + M:System.Drawing.Icon.Save(System.IO.Stream) ILLink IL2050 member - M:System.Drawing.SafeNativeMethods.Gdip.GdipCreateBitmapFromStreamICM(Interop.Ole32.IStream,System.IntPtr@) + M:System.Drawing.Image.FromStream(System.IO.Stream,System.Boolean,System.Boolean) ILLink IL2050 member - M:System.Drawing.SafeNativeMethods.Gdip.GdipCreateMetafileFromStream(Interop.Ole32.IStream,System.IntPtr@) + M:System.Drawing.Image.InitializeFromStream(System.IO.Stream) ILLink IL2050 member - M:System.Drawing.SafeNativeMethods.Gdip.GdipGetMetafileHeaderFromStream(Interop.Ole32.IStream,System.IntPtr) + M:System.Drawing.Image.Save(System.IO.Stream,System.Drawing.Imaging.ImageCodecInfo,System.Drawing.Imaging.EncoderParameters) ILLink IL2050 member - M:System.Drawing.SafeNativeMethods.Gdip.GdipLoadImageFromStream(Interop.Ole32.IStream,System.IntPtr@) + M:System.Drawing.Imaging.Metafile.#ctor(System.IO.Stream,System.IntPtr,System.Drawing.Imaging.EmfType,System.String) ILLink IL2050 member - M:System.Drawing.SafeNativeMethods.Gdip.GdipLoadImageFromStreamICM(Interop.Ole32.IStream,System.IntPtr@) + M:System.Drawing.Imaging.Metafile.#ctor(System.IO.Stream,System.IntPtr,System.Drawing.Rectangle,System.Drawing.Imaging.MetafileFrameUnit,System.Drawing.Imaging.EmfType,System.String) ILLink IL2050 member - M:System.Drawing.SafeNativeMethods.Gdip.GdipRecordMetafileStream(Interop.Ole32.IStream,System.IntPtr,System.Drawing.Imaging.EmfType,System.Drawing.RectangleF@,System.Drawing.Imaging.MetafileFrameUnit,System.String,System.IntPtr@) + M:System.Drawing.Imaging.Metafile.#ctor(System.IO.Stream,System.IntPtr,System.Drawing.RectangleF,System.Drawing.Imaging.MetafileFrameUnit,System.Drawing.Imaging.EmfType,System.String) ILLink IL2050 member - M:System.Drawing.SafeNativeMethods.Gdip.GdipRecordMetafileStream(Interop.Ole32.IStream,System.IntPtr,System.Drawing.Imaging.EmfType,System.IntPtr,System.Drawing.Imaging.MetafileFrameUnit,System.String,System.IntPtr@) + M:System.Drawing.Imaging.Metafile.#ctor(System.IO.Stream) ILLink IL2050 member - M:System.Drawing.SafeNativeMethods.Gdip.GdipRecordMetafileStreamI(Interop.Ole32.IStream,System.IntPtr,System.Drawing.Imaging.EmfType,System.Drawing.Rectangle@,System.Drawing.Imaging.MetafileFrameUnit,System.String,System.IntPtr@) - - - ILLink - IL2050 - member - M:System.Drawing.SafeNativeMethods.Gdip.GdipSaveImageToStream(System.Runtime.InteropServices.HandleRef,Interop.Ole32.IStream,System.Guid@,System.Runtime.InteropServices.HandleRef) + M:System.Drawing.Imaging.Metafile.GetMetafileHeader(System.IO.Stream) diff --git a/src/libraries/pkg/test/testPackages.proj b/src/libraries/pkg/test/testPackages.proj index 03492c30ee0f9..83ad6233b6432 100644 --- a/src/libraries/pkg/test/testPackages.proj +++ b/src/libraries/pkg/test/testPackages.proj @@ -75,7 +75,7 @@ - + From 56778f0b3bc724fe4dae87e626ebc1403db99bb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emek=20Vysok=C3=BD?= Date: Tue, 22 Jun 2021 13:20:02 +0200 Subject: [PATCH 054/107] Revert "[main] Update dependencies from 9 repositories (#54218)" (#54541) This reverts commit 04ad80aff37e2ef5eac0cc6d2a895ccf0f9d65a3. --- .config/dotnet-tools.json | 2 +- eng/Version.Details.xml | 203 +++++++++--------- eng/Versions.props | 88 ++++---- eng/common/generate-locproject.ps1 | 13 +- .../templates/job/source-index-stage1.yml | 4 - global.json | 10 +- .../src/ILLink/ILLink.Suppressions.xml | 2 +- .../src/ILLink/ILLink.Suppressions.xml | 20 +- .../src/ILLink/ILLink.Suppressions.xml | 2 +- .../src/ILLink/ILLink.Suppressions.xml | 2 +- .../src/ILLink/ILLink.Suppressions.xml | 26 ++- src/libraries/pkg/test/testPackages.proj | 2 +- 12 files changed, 176 insertions(+), 198 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 6428121a0fec7..02e0543e98b8e 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "microsoft.dotnet.xharness.cli": { - "version": "1.0.0-prerelease.21322.2", + "version": "1.0.0-prerelease.21314.1", "commands": [ "xharness" ] diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index e8995dca95d92..4177e280edf32 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,221 +1,222 @@ - + https://github.com/dotnet/icu - 59588c1257a842089d0b7df3bad1cdd69ac720e1 + d7db669b70f4dd67ec001c192f9809c218cab88b - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 4a2b475948d498b89fedef7cf890883f49bc1ea3 + - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 4a2b475948d498b89fedef7cf890883f49bc1ea3 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 4a2b475948d498b89fedef7cf890883f49bc1ea3 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 4a2b475948d498b89fedef7cf890883f49bc1ea3 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 4a2b475948d498b89fedef7cf890883f49bc1ea3 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 4a2b475948d498b89fedef7cf890883f49bc1ea3 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 4a2b475948d498b89fedef7cf890883f49bc1ea3 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 4a2b475948d498b89fedef7cf890883f49bc1ea3 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 4a2b475948d498b89fedef7cf890883f49bc1ea3 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 4a2b475948d498b89fedef7cf890883f49bc1ea3 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 4a2b475948d498b89fedef7cf890883f49bc1ea3 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 4a2b475948d498b89fedef7cf890883f49bc1ea3 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 4a2b475948d498b89fedef7cf890883f49bc1ea3 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 4a2b475948d498b89fedef7cf890883f49bc1ea3 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 4a2b475948d498b89fedef7cf890883f49bc1ea3 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 4a2b475948d498b89fedef7cf890883f49bc1ea3 https://github.com/microsoft/vstest 140434f7109d357d0158ade9e5164a4861513965 - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + 0612b036e67746930105231b605c4df9ac6ed47e - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + 0612b036e67746930105231b605c4df9ac6ed47e - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + 0612b036e67746930105231b605c4df9ac6ed47e - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + 0612b036e67746930105231b605c4df9ac6ed47e - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + 0612b036e67746930105231b605c4df9ac6ed47e - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + 0612b036e67746930105231b605c4df9ac6ed47e - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + 0612b036e67746930105231b605c4df9ac6ed47e - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + 0612b036e67746930105231b605c4df9ac6ed47e - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + 0612b036e67746930105231b605c4df9ac6ed47e - + https://github.com/dotnet/llvm-project - 4f0293e0a254a2f014643ecbe973b81f26c87fd4 + a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 - + https://github.com/dotnet/llvm-project - 4f0293e0a254a2f014643ecbe973b81f26c87fd4 + a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 - + https://github.com/dotnet/llvm-project - 4f0293e0a254a2f014643ecbe973b81f26c87fd4 + a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 - + https://github.com/dotnet/llvm-project - 4f0293e0a254a2f014643ecbe973b81f26c87fd4 + a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 - + https://github.com/dotnet/llvm-project - 4f0293e0a254a2f014643ecbe973b81f26c87fd4 + a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 - + https://github.com/dotnet/llvm-project - 4f0293e0a254a2f014643ecbe973b81f26c87fd4 + a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 - + https://github.com/dotnet/llvm-project - 4f0293e0a254a2f014643ecbe973b81f26c87fd4 + a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 - + https://github.com/dotnet/llvm-project - 4f0293e0a254a2f014643ecbe973b81f26c87fd4 + a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 https://github.com/dotnet/runtime 38017c3935de95d0335bac04f4901ddfc2718656 - + https://github.com/dotnet/runtime - f891033db5b8ebf651176a3dcc3bec74a217f85e + af5c238556e204583b129cc8f5c7338f84dc2c40 - + https://github.com/dotnet/runtime - f891033db5b8ebf651176a3dcc3bec74a217f85e + af5c238556e204583b129cc8f5c7338f84dc2c40 - + https://github.com/dotnet/runtime - f891033db5b8ebf651176a3dcc3bec74a217f85e + af5c238556e204583b129cc8f5c7338f84dc2c40 - + https://github.com/dotnet/runtime - f891033db5b8ebf651176a3dcc3bec74a217f85e + af5c238556e204583b129cc8f5c7338f84dc2c40 - + https://github.com/dotnet/runtime - f891033db5b8ebf651176a3dcc3bec74a217f85e + af5c238556e204583b129cc8f5c7338f84dc2c40 - + https://github.com/dotnet/runtime - f891033db5b8ebf651176a3dcc3bec74a217f85e + af5c238556e204583b129cc8f5c7338f84dc2c40 - + https://github.com/dotnet/runtime - f891033db5b8ebf651176a3dcc3bec74a217f85e + af5c238556e204583b129cc8f5c7338f84dc2c40 - + https://github.com/mono/linker - c739a81ba553b00df1cb2f5b9974deae996b757a + caeaf2a3fb3f636805fdd4881df4f9a539fff8f6 - + https://github.com/dotnet/xharness - 0bb4a23b1b686e8fefde9d4c860b26a8fafc303e + d6f8a4ad30908fb210390380eae97264e4fbe8ce - + https://github.com/dotnet/xharness - 0bb4a23b1b686e8fefde9d4c860b26a8fafc303e + d6f8a4ad30908fb210390380eae97264e4fbe8ce - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 85a65ea1fca1d0867f699fed44d191358270bf6a - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - f291c7f87a563f29ff2a9af7378495769d97389c + 4e5bea15eb5a9c8cf9142195b1c9c78437a5b27f - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - f291c7f87a563f29ff2a9af7378495769d97389c + 4e5bea15eb5a9c8cf9142195b1c9c78437a5b27f - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - f291c7f87a563f29ff2a9af7378495769d97389c + 4e5bea15eb5a9c8cf9142195b1c9c78437a5b27f - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - f291c7f87a563f29ff2a9af7378495769d97389c + 4e5bea15eb5a9c8cf9142195b1c9c78437a5b27f https://github.com/dotnet/emsdk defa37b05c734e025292c5747664e970cd2ac444 - + https://github.com/dotnet/hotreload-utils - 6adb8ac00a59fe409f232b8b32758aa7d10b4d1d + 25b814e010cd4796cedfbcce72a274c26928f496 - https://github.com/dotnet/runtime-assets + https://github.com/dotnet/runtime-assets 8d7b898b96cbdb868cac343e938173105287ed9e diff --git a/eng/Versions.props b/eng/Versions.props index 3bc9edaa1b14c..b62b9b4854cfc 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -49,28 +49,28 @@ 3.10.0-2.final 3.10.0-2.final - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 - 2.5.1-beta.21321.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 + 6.0.0-beta.21311.3 + 6.0.0-beta.21311.3 + 6.0.0-beta.21311.3 + 6.0.0-beta.21311.3 + 6.0.0-beta.21311.3 + 6.0.0-beta.21311.3 + 2.5.1-beta.21311.3 + 6.0.0-beta.21311.3 + 6.0.0-beta.21311.3 + 6.0.0-beta.21311.3 + 6.0.0-beta.21311.3 + 6.0.0-beta.21311.3 + 6.0.0-beta.21311.3 5.9.0-preview.2 6.0.0-alpha.1.20612.4 - 6.0.0-preview.7.21321.2 - 6.0.0-preview.7.21321.2 + 6.0.0-preview.6.21314.1 + 6.0.0-preview.6.21314.1 3.1.0 - 6.0.0-preview.7.21321.2 + 6.0.0-preview.6.21314.1 5.0.0 4.3.0 @@ -104,27 +104,27 @@ 5.0.0 5.0.0 4.8.1 - 6.0.0-preview.7.21321.2 - 6.0.0-preview.7.21321.2 + 6.0.0-preview.6.21314.1 + 6.0.0-preview.6.21314.1 4.5.4 4.5.0 - 6.0.0-preview.7.21321.2 + 6.0.0-preview.6.21314.1 6.0.0-beta.21314.1 6.0.0-beta.21307.1 - 6.0.0-beta.21314.1 - 6.0.0-beta.21314.1 - 6.0.0-beta.21314.1 - 6.0.0-beta.21314.1 - 6.0.0-beta.21314.1 - 6.0.0-beta.21314.1 - 6.0.0-beta.21314.1 - 6.0.0-beta.21314.1 + 6.0.0-beta.21307.1 + 6.0.0-beta.21307.1 + 6.0.0-beta.21307.1 + 6.0.0-beta.21307.1 + 6.0.0-beta.21307.1 + 6.0.0-beta.21307.1 + 6.0.0-beta.21307.1 + 6.0.0-beta.21307.1 - 1.0.0-prerelease.21320.4 - 1.0.0-prerelease.21320.4 - 1.0.0-prerelease.21320.4 - 1.0.0-prerelease.21320.4 + 1.0.0-prerelease.21313.4 + 1.0.0-prerelease.21313.4 + 1.0.0-prerelease.21313.4 + 1.0.0-prerelease.21313.4 16.9.0-beta1.21055.5 2.0.0-beta1.20253.1 @@ -148,9 +148,9 @@ 1.0.1-prerelease-00006 16.9.0-preview-20201201-01 - 1.0.0-prerelease.21322.2 - 1.0.0-prerelease.21322.2 - 1.0.1-alpha.0.21314.1 + 1.0.0-prerelease.21314.1 + 1.0.0-prerelease.21314.1 + 1.0.1-alpha.0.21311.1 2.4.1 2.4.2 1.3.0 @@ -161,19 +161,19 @@ 5.0.0-preview-20201009.2 - 6.0.100-preview.6.21317.4 + 6.0.100-preview.6.21310.3 $(MicrosoftNETILLinkTasksVersion) - 6.0.0-preview.7.21315.3 + 6.0.0-preview.6.21307.1 - 11.1.0-alpha.1.21314.1 - 11.1.0-alpha.1.21314.1 - 11.1.0-alpha.1.21314.1 - 11.1.0-alpha.1.21314.1 - 11.1.0-alpha.1.21314.1 - 11.1.0-alpha.1.21314.1 - 11.1.0-alpha.1.21314.1 - 11.1.0-alpha.1.21314.1 + 11.1.0-alpha.1.21308.1 + 11.1.0-alpha.1.21308.1 + 11.1.0-alpha.1.21308.1 + 11.1.0-alpha.1.21308.1 + 11.1.0-alpha.1.21308.1 + 11.1.0-alpha.1.21308.1 + 11.1.0-alpha.1.21308.1 + 11.1.0-alpha.1.21308.1 6.0.0-preview.6.21275.1 $(MicrosoftNETRuntimeEmscripten2021Nodewinx64Version) diff --git a/eng/common/generate-locproject.ps1 b/eng/common/generate-locproject.ps1 index 25e97ac00772d..de348a2e225c0 100644 --- a/eng/common/generate-locproject.ps1 +++ b/eng/common/generate-locproject.ps1 @@ -25,15 +25,8 @@ Push-Location "$SourcesDirectory" # push location for Resolve-Path -Relative to # Template files $jsonFiles = @() -$jsonTemplateFiles = Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "\.template\.config\\localize\\.+\.en\.json" } # .NET templating pattern -$jsonTemplateFiles | ForEach-Object { - $null = $_.Name -Match "(.+)\.[\w-]+\.json" # matches '[filename].[langcode].json - - $destinationFile = "$($_.Directory.FullName)\$($Matches.1).json" - $jsonFiles += Copy-Item "$($_.FullName)" -Destination $destinationFile -PassThru -} - -$jsonWinformsTemplateFiles = Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "en\\strings\.json" } # current winforms pattern +$jsonFiles += Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "\.template\.config\\localize\\en\..+\.json" } # .NET templating pattern +$jsonFiles += Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "en\\strings\.json" } # current winforms pattern $xlfFiles = @() @@ -51,7 +44,7 @@ $langXlfFiles | ForEach-Object { $xlfFiles += Copy-Item "$($_.FullName)" -Destination $destinationFile -PassThru } -$locFiles = $jsonFiles + $jsonWinformsTemplateFiles + $xlfFiles +$locFiles = $jsonFiles + $xlfFiles $locJson = @{ Projects = @( diff --git a/eng/common/templates/job/source-index-stage1.yml b/eng/common/templates/job/source-index-stage1.yml index b58d42364b9e7..6e8aa9f7f218b 100644 --- a/eng/common/templates/job/source-index-stage1.yml +++ b/eng/common/templates/job/source-index-stage1.yml @@ -7,13 +7,9 @@ parameters: binlogPath: artifacts/log/Debug/Build.binlog pool: vmImage: vs2017-win2016 - condition: '' - dependsOn: '' jobs: - job: SourceIndexStage1 - dependsOn: ${{ parameters.dependsOn }} - condition: ${{ parameters.condition }} variables: - name: SourceIndexPackageVersion value: ${{ parameters.sourceIndexPackageVersion }} diff --git a/global.json b/global.json index 433797e811628..df8f7ead63774 100644 --- a/global.json +++ b/global.json @@ -12,13 +12,13 @@ "python3": "3.7.1" }, "msbuild-sdks": { - "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21321.1", + "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21311.3", "Microsoft.DotNet.PackageValidation": "1.0.0-preview.6.21274.7", - "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21321.1", - "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21321.1", - "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21321.1", + "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21311.3", + "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21311.3", + "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21311.3", "Microsoft.Build.NoTargets": "3.0.4", "Microsoft.Build.Traversal": "3.0.23", - "Microsoft.NET.Sdk.IL": "6.0.0-preview.7.21321.2" + "Microsoft.NET.Sdk.IL": "6.0.0-preview.6.21314.1" } } diff --git a/src/libraries/System.Data.Odbc/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Data.Odbc/src/ILLink/ILLink.Suppressions.xml index 0504df00e7659..533fc1cd0ed04 100644 --- a/src/libraries/System.Data.Odbc/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Data.Odbc/src/ILLink/ILLink.Suppressions.xml @@ -5,7 +5,7 @@ ILLink IL2050 member - M:System.Data.Odbc.OdbcConnectionHandle.SetConnectionAttribute4(System.Data.Odbc.ODBC32.SQL_ATTR,System.Transactions.IDtcTransaction,System.Int32) + M:Interop.Odbc.SQLSetConnectAttrW(System.Data.Odbc.OdbcConnectionHandle,System.Data.Odbc.ODBC32.SQL_ATTR,System.Transactions.IDtcTransaction,System.Int32) ILLink diff --git a/src/libraries/System.Data.OleDb/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Data.OleDb/src/ILLink/ILLink.Suppressions.xml index 5386e4bd294fc..2e52a33c8a98e 100644 --- a/src/libraries/System.Data.OleDb/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Data.OleDb/src/ILLink/ILLink.Suppressions.xml @@ -5,25 +5,7 @@ ILLink IL2050 member - M:System.Data.OleDb.DBPropSet.SetLastErrorInfo(System.Data.OleDb.OleDbHResult) - - - ILLink - IL2050 - member - M:System.Data.OleDb.OleDbConnection.ProcessResults(System.Data.OleDb.OleDbHResult,System.Data.OleDb.OleDbConnection,System.Object) - - - ILLink - IL2050 - member - M:System.Data.OleDb.OleDbDataAdapter.FillClose(System.Boolean,System.Object) - - - ILLink - IL2050 - member - M:System.Data.OleDb.OleDbDataAdapter.FillFromADODB(System.Object,System.Object,System.String,System.Boolean) + M:System.Data.Common.UnsafeNativeMethods.GetErrorInfo(System.Int32,System.Data.Common.UnsafeNativeMethods.IErrorInfo@) ILLink diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.DirectoryServices.AccountManagement/src/ILLink/ILLink.Suppressions.xml index 7572cc7074afb..ae9db1185dd9c 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/ILLink/ILLink.Suppressions.xml @@ -5,7 +5,7 @@ ILLink IL2050 member - M:System.DirectoryServices.AccountManagement.UnsafeNativeMethods.ADsOpenObject(System.String,System.String,System.String,System.Int32,System.Guid@,System.Object@) + M:System.DirectoryServices.AccountManagement.UnsafeNativeMethods.IntADsOpenObject(System.String,System.String,System.String,System.Int32,System.Guid@,System.Object@) ILLink diff --git a/src/libraries/System.DirectoryServices/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.DirectoryServices/src/ILLink/ILLink.Suppressions.xml index ca9681a8e8f63..0e87e736269c4 100644 --- a/src/libraries/System.DirectoryServices/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.DirectoryServices/src/ILLink/ILLink.Suppressions.xml @@ -5,7 +5,7 @@ ILLink IL2050 member - M:System.DirectoryServices.Interop.UnsafeNativeMethods.ADsOpenObject(System.String,System.String,System.String,System.Int32,System.Guid@,System.Object@) + M:System.DirectoryServices.Interop.UnsafeNativeMethods.IntADsOpenObject(System.String,System.String,System.String,System.Int32,System.Guid@,System.Object@) ILLink diff --git a/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml index 3f367cccd2496..d5734aab1a2a9 100644 --- a/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml @@ -5,61 +5,67 @@ ILLink IL2050 member - M:System.Drawing.Bitmap.#ctor(System.IO.Stream,System.Boolean) + M:System.Drawing.Icon.OleCreatePictureIndirect(System.Drawing.Icon.PICTDESC,System.Guid@,System.Boolean) ILLink IL2050 member - M:System.Drawing.Icon.Save(System.IO.Stream) + M:System.Drawing.SafeNativeMethods.Gdip.GdipCreateBitmapFromStream(Interop.Ole32.IStream,System.IntPtr@) ILLink IL2050 member - M:System.Drawing.Image.FromStream(System.IO.Stream,System.Boolean,System.Boolean) + M:System.Drawing.SafeNativeMethods.Gdip.GdipCreateBitmapFromStreamICM(Interop.Ole32.IStream,System.IntPtr@) ILLink IL2050 member - M:System.Drawing.Image.InitializeFromStream(System.IO.Stream) + M:System.Drawing.SafeNativeMethods.Gdip.GdipCreateMetafileFromStream(Interop.Ole32.IStream,System.IntPtr@) ILLink IL2050 member - M:System.Drawing.Image.Save(System.IO.Stream,System.Drawing.Imaging.ImageCodecInfo,System.Drawing.Imaging.EncoderParameters) + M:System.Drawing.SafeNativeMethods.Gdip.GdipGetMetafileHeaderFromStream(Interop.Ole32.IStream,System.IntPtr) ILLink IL2050 member - M:System.Drawing.Imaging.Metafile.#ctor(System.IO.Stream,System.IntPtr,System.Drawing.Imaging.EmfType,System.String) + M:System.Drawing.SafeNativeMethods.Gdip.GdipLoadImageFromStream(Interop.Ole32.IStream,System.IntPtr@) ILLink IL2050 member - M:System.Drawing.Imaging.Metafile.#ctor(System.IO.Stream,System.IntPtr,System.Drawing.Rectangle,System.Drawing.Imaging.MetafileFrameUnit,System.Drawing.Imaging.EmfType,System.String) + M:System.Drawing.SafeNativeMethods.Gdip.GdipLoadImageFromStreamICM(Interop.Ole32.IStream,System.IntPtr@) ILLink IL2050 member - M:System.Drawing.Imaging.Metafile.#ctor(System.IO.Stream,System.IntPtr,System.Drawing.RectangleF,System.Drawing.Imaging.MetafileFrameUnit,System.Drawing.Imaging.EmfType,System.String) + M:System.Drawing.SafeNativeMethods.Gdip.GdipRecordMetafileStream(Interop.Ole32.IStream,System.IntPtr,System.Drawing.Imaging.EmfType,System.Drawing.RectangleF@,System.Drawing.Imaging.MetafileFrameUnit,System.String,System.IntPtr@) ILLink IL2050 member - M:System.Drawing.Imaging.Metafile.#ctor(System.IO.Stream) + M:System.Drawing.SafeNativeMethods.Gdip.GdipRecordMetafileStream(Interop.Ole32.IStream,System.IntPtr,System.Drawing.Imaging.EmfType,System.IntPtr,System.Drawing.Imaging.MetafileFrameUnit,System.String,System.IntPtr@) ILLink IL2050 member - M:System.Drawing.Imaging.Metafile.GetMetafileHeader(System.IO.Stream) + M:System.Drawing.SafeNativeMethods.Gdip.GdipRecordMetafileStreamI(Interop.Ole32.IStream,System.IntPtr,System.Drawing.Imaging.EmfType,System.Drawing.Rectangle@,System.Drawing.Imaging.MetafileFrameUnit,System.String,System.IntPtr@) + + + ILLink + IL2050 + member + M:System.Drawing.SafeNativeMethods.Gdip.GdipSaveImageToStream(System.Runtime.InteropServices.HandleRef,Interop.Ole32.IStream,System.Guid@,System.Runtime.InteropServices.HandleRef) diff --git a/src/libraries/pkg/test/testPackages.proj b/src/libraries/pkg/test/testPackages.proj index 83ad6233b6432..03492c30ee0f9 100644 --- a/src/libraries/pkg/test/testPackages.proj +++ b/src/libraries/pkg/test/testPackages.proj @@ -75,7 +75,7 @@ - + From fe89236891914593e6181102c80f5c36561e1753 Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Tue, 22 Jun 2021 15:02:45 +0200 Subject: [PATCH 055/107] [WIP][wasm][testing] remote loop network tests via xharness and websocket (#54289) Makes it possible to run LoopbackServer tests with WASM - conditional compilation of LoopbackServer via #if TARGET_BROWSER - minimal implementation of WebSocketStream for the unit tests - simple SocketWrapper class to abstract Socket and WebSocket close/dispose/shutdown - added handling of CORS headers and pre-flight requests as necessary - new xharness web server middleware - adding it to helix payload --- .../tests/System/Net/Configuration.Http.cs | 4 + .../System/Net/Http/GenericLoopbackServer.cs | 52 +++- .../Net/Http/Http2LoopbackConnection.cs | 12 +- .../System/Net/Http/Http2LoopbackServer.cs | 7 +- .../System/Net/Http/Http3LoopbackServer.cs | 2 +- .../Net/Http/HttpAgnosticLoopbackServer.cs | 10 +- .../HttpClientHandlerTest.Authentication.cs | 10 - .../HttpClientHandlerTest.Cancellation.cs | 1 + .../Net/Http/HttpClientHandlerTest.Cookies.cs | 16 + .../HttpClientHandlerTest.Decompression.cs | 8 +- .../System/Net/Http/HttpClientHandlerTest.cs | 151 +++++++--- .../tests/System/Net/Http/LoopbackServer.cs | 152 ++++++++-- .../System/Net/Http/ResponseStreamTest.cs | 5 +- .../Net/Prerequisites/LocalEchoServer.props | 7 +- .../Net/Prerequisites/NetCoreServer.sln | 25 -- .../Net/Prerequisites/NetTestServers.sln | 30 ++ .../RemoteLoopServer/GenericHandler.cs | 47 +++ .../Handlers/RemoteLoopHandler.cs | 161 ++++++++++ .../Prerequisites/RemoteLoopServer/Program.cs | 23 ++ .../RemoteLoopServer/RemoteLoopServer.csproj | 15 + .../Prerequisites/RemoteLoopServer/Startup.cs | 23 ++ .../appsettings.Development.json | 9 + .../RemoteLoopServer/appsettings.json | 8 + .../System/Net/WebSockets/WebSocketStream.cs | 285 ++++++++++++++++++ .../HttpClientHandlerTest.AltSvc.cs | 2 +- .../HttpClientHandlerTest.Headers.cs | 33 +- .../HttpClientHandlerTest.Http1.cs | 1 - .../HttpClientHandlerTest.RequestRetry.cs | 1 - .../HttpClientMiniStressTest.cs | 1 + .../tests/FunctionalTests/HttpClientTest.cs | 42 +-- .../tests/FunctionalTests/HttpContentTest.cs | 21 +- .../FunctionalTests/HttpRequestMessageTest.cs | 10 +- .../FunctionalTests/SocketsHttpHandlerTest.cs | 49 ++- .../tests/FunctionalTests/SocksProxyTest.cs | 6 +- .../System.Net.Http.Functional.Tests.csproj | 5 + .../tests/CloseTest.cs | 2 +- .../tests/ConnectTest.cs | 10 +- .../tests/DeflateTests.cs | 1 - .../tests/SendReceiveTest.cs | 2 +- .../System.Net.WebSockets.Client.Tests.csproj | 5 + src/libraries/sendtohelixhelp.proj | 2 + 41 files changed, 1028 insertions(+), 228 deletions(-) delete mode 100644 src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer.sln create mode 100644 src/libraries/Common/tests/System/Net/Prerequisites/NetTestServers.sln create mode 100644 src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/GenericHandler.cs create mode 100644 src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/Handlers/RemoteLoopHandler.cs create mode 100644 src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/Program.cs create mode 100644 src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/RemoteLoopServer.csproj create mode 100644 src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/Startup.cs create mode 100644 src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/appsettings.Development.json create mode 100644 src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/appsettings.json create mode 100644 src/libraries/Common/tests/System/Net/WebSockets/WebSocketStream.cs diff --git a/src/libraries/Common/tests/System/Net/Configuration.Http.cs b/src/libraries/Common/tests/System/Net/Configuration.Http.cs index e43c2c0e385e9..ae1481c56061c 100644 --- a/src/libraries/Common/tests/System/Net/Configuration.Http.cs +++ b/src/libraries/Common/tests/System/Net/Configuration.Http.cs @@ -45,12 +45,15 @@ public static partial class Http public static string EchoClientCertificateRemoteServer => GetValue("DOTNET_TEST_HTTPHOST_ECHOCLIENTCERT", "https://corefx-net-tls.azurewebsites.net/EchoClientCertificate.ashx"); public static string Http2ForceUnencryptedLoopback => GetValue("DOTNET_TEST_HTTP2_FORCEUNENCRYPTEDLOOPBACK"); + public static string RemoteLoopHost => GetValue("DOTNET_TEST_REMOTE_LOOP_HOST"); + private const string EchoHandler = "Echo.ashx"; private const string EmptyContentHandler = "EmptyContent.ashx"; private const string RedirectHandler = "Redirect.ashx"; private const string VerifyUploadHandler = "VerifyUpload.ashx"; private const string DeflateHandler = "Deflate.ashx"; private const string GZipHandler = "GZip.ashx"; + private const string RemoteLoopHandler = "RemoteLoop"; public static readonly Uri RemoteEchoServer = new Uri("http://" + Host + "/" + EchoHandler); public static readonly Uri SecureRemoteEchoServer = new Uri("https://" + SecureHost + "/" + EchoHandler); @@ -67,6 +70,7 @@ public static partial class Http public static readonly Uri RemoteGZipServer = new Uri("http://" + Host + "/" + GZipHandler); public static readonly Uri Http2RemoteDeflateServer = new Uri("https://" + Http2Host + "/" + DeflateHandler); public static readonly Uri Http2RemoteGZipServer = new Uri("https://" + Http2Host + "/" + GZipHandler); + public static Uri RemoteLoopServer => new Uri("ws://" + RemoteLoopHost + "/" + RemoteLoopHandler); public static readonly object[][] EchoServers = EchoServerList.Select(x => new object[] { x }).ToArray(); public static readonly object[][] VerifyUploadServers = { new object[] { RemoteVerifyUploadServer }, new object[] { SecureRemoteVerifyUploadServer }, new object[] { Http2RemoteVerifyUploadServer } }; diff --git a/src/libraries/Common/tests/System/Net/Http/GenericLoopbackServer.cs b/src/libraries/Common/tests/System/Net/Http/GenericLoopbackServer.cs index d3095bd7a2045..c145b6aa3d493 100644 --- a/src/libraries/Common/tests/System/Net/Http/GenericLoopbackServer.cs +++ b/src/libraries/Common/tests/System/Net/Http/GenericLoopbackServer.cs @@ -9,6 +9,8 @@ using System.Security.Cryptography.X509Certificates; using System.IO; using System.Net.Sockets; +using System.Net.WebSockets; +using System.Threading; namespace System.Net.Test.Common { @@ -20,7 +22,7 @@ public abstract class LoopbackServerFactory public abstract GenericLoopbackServer CreateServer(GenericLoopbackOptions options = null); public abstract Task CreateServerAsync(Func funcAsync, int millisecondsTimeout = 60_000, GenericLoopbackOptions options = null); - public abstract Task CreateConnectionAsync(Socket socket, Stream stream, GenericLoopbackOptions options = null); + public abstract Task CreateConnectionAsync(SocketWrapper socket, Stream stream, GenericLoopbackOptions options = null); public abstract Version Version { get; } @@ -59,6 +61,54 @@ public Task AcceptConnectionSendResponseAndCloseAsync(HttpStatu } } + public sealed class SocketWrapper : IDisposable + { + private Socket _socket; + private WebSocket _websocket; + + public SocketWrapper(Socket socket) + { + _socket = socket; + } + public SocketWrapper(WebSocket websocket) + { + _websocket = websocket; + } + + public void Dispose() + { + _socket?.Dispose(); + _websocket?.Dispose(); + } + public void Close() + { + _socket?.Close(); + CloseWebSocket(); + } + + public void Shutdown(SocketShutdown how) + { + _socket?.Shutdown(how); + CloseWebSocket(); + } + + private void CloseWebSocket() + { + if (_websocket != null && (_websocket.State == WebSocketState.Open || _websocket.State == WebSocketState.Connecting || _websocket.State == WebSocketState.None)) + { + try + { + var task = _websocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "closing remoteLoop", CancellationToken.None); + // Block and wait for the task to complete synchronously + Task.WaitAll(task); + } + catch (Exception) + { + } + } + } + } + public abstract class GenericLoopbackConnection : IDisposable { public abstract void Dispose(); diff --git a/src/libraries/Common/tests/System/Net/Http/Http2LoopbackConnection.cs b/src/libraries/Common/tests/System/Net/Http/Http2LoopbackConnection.cs index 01fa9d4e697b5..5dcbc2d3863e3 100644 --- a/src/libraries/Common/tests/System/Net/Http/Http2LoopbackConnection.cs +++ b/src/libraries/Common/tests/System/Net/Http/Http2LoopbackConnection.cs @@ -20,7 +20,7 @@ public class Http2LoopbackConnection : GenericLoopbackConnection { public const string Http2Prefix = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"; - private Socket _connectionSocket; + private SocketWrapper _connectionSocket; private Stream _connectionStream; private TaskCompletionSource _ignoredSettingsAckPromise; private bool _ignoreWindowUpdates; @@ -34,19 +34,19 @@ public class Http2LoopbackConnection : GenericLoopbackConnection public Stream Stream => _connectionStream; public Task SettingAckWaiter => _ignoredSettingsAckPromise?.Task; - private Http2LoopbackConnection(Socket socket, Stream stream, TimeSpan timeout) + private Http2LoopbackConnection(SocketWrapper socket, Stream stream, TimeSpan timeout) { _connectionSocket = socket; _connectionStream = stream; _timeout = timeout; } - public static Task CreateAsync(Socket socket, Stream stream, Http2Options httpOptions) + public static Task CreateAsync(SocketWrapper socket, Stream stream, Http2Options httpOptions) { return CreateAsync(socket, stream, httpOptions, Http2LoopbackServer.Timeout); } - public static async Task CreateAsync(Socket socket, Stream stream, Http2Options httpOptions, TimeSpan timeout) + public static async Task CreateAsync(SocketWrapper socket, Stream stream, Http2Options httpOptions, TimeSpan timeout) { if (httpOptions.UseSsl) { @@ -230,9 +230,9 @@ private async Task ReadFrameAsync(CancellationToken cancellationToken) } // Reset and return underlying networking objects. - public (Socket, Stream) ResetNetwork() + public (SocketWrapper, Stream) ResetNetwork() { - Socket oldSocket = _connectionSocket; + SocketWrapper oldSocket = _connectionSocket; Stream oldStream = _connectionStream; _connectionSocket = null; _connectionStream = null; diff --git a/src/libraries/Common/tests/System/Net/Http/Http2LoopbackServer.cs b/src/libraries/Common/tests/System/Net/Http/Http2LoopbackServer.cs index 45c2edf345951..45df14c0380ea 100644 --- a/src/libraries/Common/tests/System/Net/Http/Http2LoopbackServer.cs +++ b/src/libraries/Common/tests/System/Net/Http/Http2LoopbackServer.cs @@ -91,9 +91,10 @@ public async Task AcceptConnectionAsync(TimeSpan? timeo Socket connectionSocket = await _listenSocket.AcceptAsync().ConfigureAwait(false); var stream = new NetworkStream(connectionSocket, ownsSocket: true); + var wrapper = new SocketWrapper(connectionSocket); Http2LoopbackConnection connection = - timeout != null ? await Http2LoopbackConnection.CreateAsync(connectionSocket, stream, _options, timeout.Value).ConfigureAwait(false) : - await Http2LoopbackConnection.CreateAsync(connectionSocket, stream, _options).ConfigureAwait(false); + timeout != null ? await Http2LoopbackConnection.CreateAsync(wrapper, stream, _options, timeout.Value).ConfigureAwait(false) : + await Http2LoopbackConnection.CreateAsync(wrapper, stream, _options).ConfigureAwait(false); _connections.Add(connection); return connection; @@ -201,7 +202,7 @@ public override GenericLoopbackServer CreateServer(GenericLoopbackOptions option return Http2LoopbackServer.CreateServer(CreateOptions(options)); } - public override async Task CreateConnectionAsync(Socket socket, Stream stream, GenericLoopbackOptions options = null) + public override async Task CreateConnectionAsync(SocketWrapper socket, Stream stream, GenericLoopbackOptions options = null) { return await Http2LoopbackConnection.CreateAsync(socket, stream, CreateOptions(options)).ConfigureAwait(false); } diff --git a/src/libraries/Common/tests/System/Net/Http/Http3LoopbackServer.cs b/src/libraries/Common/tests/System/Net/Http/Http3LoopbackServer.cs index cf83b893ff739..b84393a88b1d8 100644 --- a/src/libraries/Common/tests/System/Net/Http/Http3LoopbackServer.cs +++ b/src/libraries/Common/tests/System/Net/Http/Http3LoopbackServer.cs @@ -97,7 +97,7 @@ public override async Task CreateServerAsync(Func CreateConnectionAsync(Socket socket, Stream stream, GenericLoopbackOptions options = null) + public override Task CreateConnectionAsync(SocketWrapper socket, Stream stream, GenericLoopbackOptions options = null) { // TODO: make a new overload that takes a MultiplexedConnection. // This method is always unacceptable to call for HTTP/3. diff --git a/src/libraries/Common/tests/System/Net/Http/HttpAgnosticLoopbackServer.cs b/src/libraries/Common/tests/System/Net/Http/HttpAgnosticLoopbackServer.cs index ae436df429a1d..88a8071153e6c 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpAgnosticLoopbackServer.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpAgnosticLoopbackServer.cs @@ -87,13 +87,13 @@ public override async Task EstablishGenericConnection if (sslStream.NegotiatedApplicationProtocol == SslApplicationProtocol.Http2) { // Do not pass original options so the CreateConnectionAsync won't try to do ALPN again. - return connection = await Http2LoopbackServerFactory.Singleton.CreateConnectionAsync(socket, stream, options).ConfigureAwait(false); + return connection = await Http2LoopbackServerFactory.Singleton.CreateConnectionAsync(new SocketWrapper(socket), stream, options).ConfigureAwait(false); } if (sslStream.NegotiatedApplicationProtocol == SslApplicationProtocol.Http11 || sslStream.NegotiatedApplicationProtocol == default) { // Do not pass original options so the CreateConnectionAsync won't try to do ALPN again. - return connection = await Http11LoopbackServerFactory.Singleton.CreateConnectionAsync(socket, stream, options).ConfigureAwait(false); + return connection = await Http11LoopbackServerFactory.Singleton.CreateConnectionAsync(new SocketWrapper(socket), stream, options).ConfigureAwait(false); } else { @@ -103,11 +103,11 @@ public override async Task EstablishGenericConnection if (_options.ClearTextVersion == HttpVersion.Version11) { - return connection = await Http11LoopbackServerFactory.Singleton.CreateConnectionAsync(socket, stream, options).ConfigureAwait(false); + return connection = await Http11LoopbackServerFactory.Singleton.CreateConnectionAsync(new SocketWrapper(socket), stream, options).ConfigureAwait(false); } else if (_options.ClearTextVersion == HttpVersion.Version20) { - return connection = await Http2LoopbackServerFactory.Singleton.CreateConnectionAsync(socket, stream, options).ConfigureAwait(false); + return connection = await Http2LoopbackServerFactory.Singleton.CreateConnectionAsync(new SocketWrapper(socket), stream, options).ConfigureAwait(false); } else { @@ -187,7 +187,7 @@ public override GenericLoopbackServer CreateServer(GenericLoopbackOptions option return HttpAgnosticLoopbackServer.CreateServer(CreateOptions(options)); } - public override Task CreateConnectionAsync(Socket socket, Stream stream, GenericLoopbackOptions options = null) + public override Task CreateConnectionAsync(SocketWrapper socket, Stream stream, GenericLoopbackOptions options = null) { // This method is always unacceptable to call for an agnostic server. throw new NotImplementedException("HttpAgnosticLoopbackServerFactory cannot create connection."); diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Authentication.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Authentication.cs index 4df4100a8ed41..f7ccc3127e9ab 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Authentication.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Authentication.cs @@ -104,7 +104,6 @@ public static IEnumerable Authentication_SocketsHttpHandler_TestData() [Theory] [MemberData(nameof(Authentication_TestData))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] public async Task HttpClientHandler_Authentication_Succeeds(string authenticateHeader, bool result) { if (PlatformDetection.IsWindowsNanoServer) @@ -144,7 +143,6 @@ public async Task HttpClientHandler_MultipleAuthenticateHeaders_WithSameAuth_Suc [Theory] [InlineData("WWW-Authenticate: Basic realm=\"hello\"\r\nWWW-Authenticate: Digest realm=\"hello\", nonce=\"hello\", algorithm=MD5\r\n")] [InlineData("WWW-Authenticate: Digest realm=\"hello\", nonce=\"hello\", algorithm=MD5\r\nWWW-Authenticate: Basic realm=\"hello\"\r\n")] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] public async Task HttpClientHandler_MultipleAuthenticateHeaders_Succeeds(string authenticateHeader) { if (PlatformDetection.IsWindowsNanoServer) @@ -164,7 +162,6 @@ await LoopbackServer.CreateServerAsync(async (server, url) => [Theory] [InlineData("WWW-Authenticate: Basic realm=\"hello\"\r\nWWW-Authenticate: NTLM\r\n", "Basic", "Negotiate")] [InlineData("WWW-Authenticate: Basic realm=\"hello\"\r\nWWW-Authenticate: Digest realm=\"hello\", nonce=\"hello\", algorithm=MD5\r\nWWW-Authenticate: NTLM\r\n", "Digest", "Negotiate")] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] public async Task HttpClientHandler_MultipleAuthenticateHeaders_PicksSupported(string authenticateHeader, string supportedAuth, string unsupportedAuth) { if (PlatformDetection.IsWindowsNanoServer) @@ -190,7 +187,6 @@ await LoopbackServer.CreateServerAsync(async (server, url) => [Theory] [InlineData("WWW-Authenticate: Basic realm=\"hello\"\r\n")] [InlineData("WWW-Authenticate: Digest realm=\"hello\", nonce=\"testnonce\"\r\n")] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] public async Task HttpClientHandler_IncorrectCredentials_Fails(string authenticateHeader) { var options = new LoopbackServer.Options { Domain = Domain, Username = Username, Password = Password }; @@ -228,7 +224,6 @@ public static IEnumerable Authentication_TestData() [InlineData("NTLM")] [InlineData("Kerberos")] [InlineData("Negotiate")] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] public async Task PreAuthenticate_NoPreviousAuthenticatedRequests_NoCredentialsSent(string credCacheScheme) { const int NumRequests = 3; @@ -271,7 +266,6 @@ await LoopbackServer.CreateClientAndServerAsync(async uri => [Theory] [InlineData(null, "WWW-Authenticate: Basic realm=\"hello\"\r\n")] [InlineData("Basic", "WWW-Authenticate: Basic realm=\"hello\"\r\n")] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] public async Task PreAuthenticate_FirstRequestNoHeaderAndAuthenticates_SecondRequestPreauthenticates(string credCacheScheme, string authResponse) { await LoopbackServer.CreateClientAndServerAsync(async uri => @@ -364,7 +358,6 @@ await LoopbackServer.CreateClientAndServerAsync(async uri => [InlineData((HttpStatusCode)508)] // LoopDetected [InlineData((HttpStatusCode)510)] // NotExtended [InlineData((HttpStatusCode)511)] // NetworkAuthenticationRequired - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] public async Task PreAuthenticate_FirstRequestNoHeader_SecondRequestVariousStatusCodes_ThirdRequestPreauthenticates(HttpStatusCode statusCode) { const string AuthResponse = "WWW-Authenticate: Basic realm=\"hello\"\r\n"; @@ -408,7 +401,6 @@ await LoopbackServer.CreateClientAndServerAsync(async uri => [InlineData("/something/hello.html", "/world.html", false)] [InlineData("/something/hello.html", "/another/", false)] [InlineData("/something/hello.html", "/another/hello.html", false)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] public async Task PreAuthenticate_AuthenticatedUrl_ThenTryDifferentUrl_SendsAuthHeaderOnlyIfPrefixMatches( string originalRelativeUri, string secondRelativeUri, bool expectedAuthHeader) { @@ -448,7 +440,6 @@ await LoopbackServer.CreateClientAndServerAsync(async uri => } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] public async Task PreAuthenticate_SuccessfulBasicButThenFails_DoesntLoopInfinitely() { await LoopbackServer.CreateClientAndServerAsync(async uri => @@ -487,7 +478,6 @@ await LoopbackServer.CreateClientAndServerAsync(async uri => } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] public async Task PreAuthenticate_SuccessfulBasic_ThenDigestChallenged() { if (IsWinHttpHandler) diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cancellation.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cancellation.cs index d246ca53e9303..410d2a6987b60 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cancellation.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cancellation.cs @@ -329,6 +329,7 @@ await LoopbackServerFactory.CreateServerAsync(async (server, url) => } [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "MaxConnectionsPerServer is not supported on Browser")] public async Task MaxConnectionsPerServer_WaitingConnectionsAreCancelable() { if (LoopbackServerFactory.Version >= HttpVersion20.Value) diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cookies.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cookies.cs index e722e8bb37c51..dbb1382c9aaf5 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cookies.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cookies.cs @@ -63,6 +63,7 @@ await LoopbackServerFactory.CreateClientAndServerAsync( [Theory] [MemberData(nameof(CookieNamesValuesAndUseCookies))] + [SkipOnPlatform(TestPlatforms.Browser, "CookieContainer is not supported on Browser")] public async Task GetAsync_SetCookieContainer_CookieSent(string cookieName, string cookieValue, bool useCookies) { await LoopbackServerFactory.CreateClientAndServerAsync( @@ -92,6 +93,7 @@ await LoopbackServerFactory.CreateClientAndServerAsync( } [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "CookieContainer is not supported on Browser")] public async Task GetAsync_SetCookieContainerMultipleCookies_CookiesSent() { var cookies = new Cookie[] @@ -211,6 +213,7 @@ private string GetCookieValue(HttpRequestData request) } [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "CookieContainer is not supported on Browser")] public async Task GetAsync_SetCookieContainerAndCookieHeader_BothCookiesSent() { await LoopbackServerFactory.CreateServerAsync(async (server, url) => @@ -238,6 +241,7 @@ await LoopbackServerFactory.CreateServerAsync(async (server, url) => } [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "CookieContainer is not supported on Browser")] public async Task GetAsync_SetCookieContainerAndMultipleCookieHeaders_BothCookiesSent() { await LoopbackServerFactory.CreateServerAsync(async (server, url) => @@ -289,6 +293,7 @@ await LoopbackServerFactory.CreateServerAsync(async (server, url) => } [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "CookieContainer is not supported on Browser")] public async Task GetAsyncWithRedirect_SetCookieContainer_CorrectCookiesSent() { const string path1 = "/foo"; @@ -329,6 +334,7 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async url => [Theory] [MemberData(nameof(CookieNamesValuesAndUseCookies))] + [SkipOnPlatform(TestPlatforms.Browser, "CookieContainer is not supported on Browser")] public async Task GetAsync_ReceiveSetCookieHeader_CookieAdded(string cookieName, string cookieValue, bool useCookies) { await LoopbackServerFactory.CreateServerAsync(async (server, url) => @@ -360,6 +366,7 @@ await LoopbackServerFactory.CreateServerAsync(async (server, url) => } [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "CookieContainer is not supported on Browser")] public async Task GetAsync_ReceiveMultipleSetCookieHeaders_CookieAdded() { await LoopbackServerFactory.CreateServerAsync(async (server, url) => @@ -399,6 +406,7 @@ await LoopbackServerFactory.CreateServerAsync(async (server, url) => // the cookie should be added with Path=/path. // ConditionalFact: CookieContainer does not follow RFC6265 on .NET Framework, therefore the (WinHttpHandler) test is expected to fail [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotNetFramework))] + [SkipOnPlatform(TestPlatforms.Browser, "CookieContainer is not supported on Browser")] public async Task GetAsync_NoPathDefined_CookieAddedWithDefaultPath() { await LoopbackServerFactory.CreateServerAsync(async (server, serverUrl) => @@ -428,6 +436,7 @@ await LoopbackServerFactory.CreateServerAsync(async (server, serverUrl) => // these cookies should be accepted by the client. // ConditionalFact: CookieContainer does not follow RFC6265 on .NET Framework, therefore the (WinHttpHandler) test is expected to fail [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotNetFramework))] + [SkipOnPlatform(TestPlatforms.Browser, "CookieContainer is not supported on Browser")] public async Task GetAsync_CookiePathDoesNotMatchRequestPath_CookieAccepted() { await LoopbackServerFactory.CreateServerAsync(async (server, serverUrl) => @@ -459,6 +468,7 @@ await LoopbackServerFactory.CreateServerAsync(async (server, serverUrl) => // https://github.com/dotnet/runtime/issues/26141#issuecomment-612097147 // ConditionalFact: CookieContainer does not follow RFC6265 on .NET Framework, therefore the (WinHttpHandler) test is expected to fail [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotNetFramework))] + [SkipOnPlatform(TestPlatforms.Browser, "CookieContainer is not supported on Browser")] public async Task GetAsync_Redirect_CookiesArePreserved() { HttpClientHandler handler = CreateHttpClientHandler(); @@ -501,6 +511,7 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async serverUrl => } [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "CookieContainer is not supported on Browser")] public async Task GetAsync_ReceiveSetCookieHeader_CookieUpdated() { const string newCookieValue = "789"; @@ -528,6 +539,7 @@ await LoopbackServerFactory.CreateServerAsync(async (server, url) => } [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "CookieContainer is not supported on Browser")] public async Task GetAsync_ReceiveSetCookieHeader_CookieRemoved() { await LoopbackServerFactory.CreateServerAsync(async (server, url) => @@ -551,6 +563,7 @@ await LoopbackServerFactory.CreateServerAsync(async (server, url) => } [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "CookieContainer is not supported on Browser")] public async Task GetAsync_ReceiveInvalidSetCookieHeader_ValidCookiesAdded() { await LoopbackServerFactory.CreateServerAsync(async (server, url) => @@ -585,6 +598,7 @@ await LoopbackServerFactory.CreateServerAsync(async (server, url) => } [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "CookieContainer is not supported on Browser")] public async Task GetAsyncWithRedirect_ReceiveSetCookie_CookieSent() { const string path1 = "/foo"; @@ -638,6 +652,7 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async url => } [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "CookieContainer is not supported on Browser")] public async Task GetAsyncWithBasicAuth_ReceiveSetCookie_CookieSent() { if (IsWinHttpHandler) @@ -757,6 +772,7 @@ public abstract class HttpClientHandlerTest_Cookies_Http11 : HttpClientHandlerTe public HttpClientHandlerTest_Cookies_Http11(ITestOutputHelper output) : base(output) { } [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "CookieContainer is not supported on Browser")] public async Task GetAsync_ReceiveMultipleSetCookieHeaders_CookieAdded() { await LoopbackServer.CreateServerAsync(async (server, url) => diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Decompression.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Decompression.cs index 6cf46633cc69b..f53b6fcb910f8 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Decompression.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Decompression.cs @@ -46,7 +46,7 @@ public static IEnumerable RemoteServersAndCompressionUris() [InlineData("deflate", true)] [InlineData("br", false)] [InlineData("br", true)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "AutomaticDecompression not supported on Browser")] public async Task DecompressedResponse_MethodSpecified_DecompressedContentReturned(string encodingName, bool all) { Func compress; @@ -136,7 +136,7 @@ public static IEnumerable DecompressedResponse_MethodNotSpecified_Orig [Theory] [MemberData(nameof(DecompressedResponse_MethodNotSpecified_OriginalContentReturned_MemberData))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "AutomaticDecompression not supported on Browser")] public async Task DecompressedResponse_MethodNotSpecified_OriginalContentReturned( string encodingName, Func compress, DecompressionMethods methods) { @@ -263,7 +263,7 @@ public async Task GetAsync_SetAutomaticDecompression_HeadersRemoved(Configuratio [InlineData(DecompressionMethods.Deflate, "deflate", "gzip")] [InlineData(DecompressionMethods.Deflate, "deflate", "br")] [InlineData(DecompressionMethods.GZip | DecompressionMethods.Deflate, "gzip, deflate", "gzip, deflate")] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "AutomaticDecompression not supported on Browser")] public async Task GetAsync_SetAutomaticDecompression_AcceptEncodingHeaderSentWithNoDuplicates( DecompressionMethods methods, string encodings, @@ -316,7 +316,7 @@ await LoopbackServer.CreateServerAsync(async (server, url) => #endif [InlineData(DecompressionMethods.GZip | DecompressionMethods.Deflate, "gzip; q=1.0, deflate; q=1.0", "")] [InlineData(DecompressionMethods.GZip | DecompressionMethods.Deflate, "gzip; q=1.0", "deflate")] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "AutomaticDecompression not supported on Browser")] public async Task GetAsync_SetAutomaticDecompression_AcceptEncodingHeaderSentWithQualityWeightingsNoDuplicates( DecompressionMethods methods, string manualAcceptEncodingHeaderValues, diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs index 06e80b66b00be..33392864210a8 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs @@ -39,7 +39,7 @@ public void CookieContainer_SetNull_ThrowsArgumentNullException() } } - [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "Credentials is not supported on Browser")] public void Ctor_ExpectedDefaultPropertyValues_CommonPlatform() { using (HttpClientHandler handler = CreateHttpClientHandler()) @@ -62,6 +62,7 @@ public void Ctor_ExpectedDefaultPropertyValues_CommonPlatform() } [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "MaxResponseHeadersLength is not supported on Browser")] public void Ctor_ExpectedDefaultPropertyValues() { using (HttpClientHandler handler = CreateHttpClientHandler()) @@ -79,6 +80,7 @@ public void Ctor_ExpectedDefaultPropertyValues() } [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "Credentials is not supported on Browser")] public void Credentials_SetGet_Roundtrips() { using (HttpClientHandler handler = CreateHttpClientHandler()) @@ -99,6 +101,7 @@ public void Credentials_SetGet_Roundtrips() [Theory] [InlineData(-1)] [InlineData(0)] + [SkipOnPlatform(TestPlatforms.Browser, "MaxAutomaticRedirections not supported on Browser")] public void MaxAutomaticRedirections_InvalidValue_Throws(int redirects) { using (HttpClientHandler handler = CreateHttpClientHandler()) @@ -148,6 +151,7 @@ public void Properties_AddItemToDictionary_ItemPresent() } [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "ServerCertificateCustomValidationCallback not supported on Browser")] public async Task GetAsync_IPv6LinkLocalAddressUri_Success() { if (IsWinHttpHandler && UseVersion >= HttpVersion20.Value) @@ -195,7 +199,10 @@ public async Task GetAsync_IPBasedUri_Success(IPAddress address) } using HttpClientHandler handler = CreateHttpClientHandler(); - handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates; + if (PlatformDetection.IsNotBrowser) + { + handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates; + } using HttpClient client = CreateHttpClient(handler); @@ -224,6 +231,7 @@ public static IEnumerable GetAsync_IPBasedUri_Success_MemberData() [Theory] [InlineData("[::1234]")] [InlineData("[::1234]:8080")] + [SkipOnPlatform(TestPlatforms.Browser, "Proxy not supported on Browser")] public async Task GetAsync_IPv6AddressInHostHeader_CorrectlyFormatted(string host) { string ipv6Address = "http://" + host; @@ -254,7 +262,9 @@ await LoopbackServer.CreateClientAndServerAsync(async proxyUri => public static IEnumerable SecureAndNonSecure_IPBasedUri_MemberData() => from address in new[] { IPAddress.Loopback, IPAddress.IPv6Loopback } - from useSsl in BoolValues + from useSsl in BoolValues + // we could not create SslStream in browser, [ActiveIssue("https://github.com/dotnet/runtime/issues/37669", TestPlatforms.Browser)] + where PlatformDetection.IsNotBrowser || !useSsl select new object[] { address, useSsl }; [Theory] @@ -267,7 +277,7 @@ public async Task GetAsync_SecureAndNonSecureIPBasedUri_CorrectlyFormatted(IPAdd return; } - var options = new LoopbackServer.Options { Address = address, UseSsl= useSsl }; + var options = new LoopbackServer.Options { Address = address, UseSsl = useSsl }; bool connectionAccepted = false; string host = ""; @@ -277,8 +287,9 @@ await LoopbackServer.CreateClientAndServerAsync(async url => using (HttpClientHandler handler = CreateHttpClientHandler()) using (HttpClient client = CreateHttpClient(handler)) { - if (useSsl) + if (useSsl && PlatformDetection.IsNotBrowser) { + // we could not create SslStream in browser, [ActiveIssue("https://github.com/dotnet/runtime/issues/37669", TestPlatforms.Browser)] handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates; } try { await client.GetAsync(url); } catch { } @@ -296,6 +307,7 @@ await LoopbackServer.CreateClientAndServerAsync(async url => [Theory] [InlineData("WWW-Authenticate", "CustomAuth")] [InlineData("", "")] // RFC7235 requires servers to send this header with 401 but some servers don't. + [SkipOnPlatform(TestPlatforms.Browser, "Credentials is not supported on Browser")] public async Task GetAsync_ServerNeedsNonStandardAuthAndSetCredential_StatusCodeUnauthorized(string authHeadrName, string authHeaderValue) { if (IsWinHttpHandler && UseVersion >= HttpVersion20.Value) @@ -328,6 +340,7 @@ await LoopbackServerFactory.CreateServerAsync(async (server, url) => [InlineData("nocolon")] [InlineData("no colon")] [InlineData("Content-Length ")] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54160", TestPlatforms.Browser)] public async Task GetAsync_InvalidHeaderNameValue_ThrowsHttpRequestException(string invalidHeader) { if (UseVersion == HttpVersion30) @@ -341,7 +354,7 @@ await LoopbackServer.CreateClientAndServerAsync(async uri => { await Assert.ThrowsAsync(() => client.GetStringAsync(uri)); } - }, server => server.AcceptConnectionSendCustomResponseAndCloseAsync($"HTTP/1.1 200 OK\r\n{invalidHeader}\r\nContent-Length: 11\r\n\r\nhello world")); + }, server => server.AcceptConnectionSendCustomResponseAndCloseAsync($"HTTP/1.1 200 OK\r\n{invalidHeader}\r\n{LoopbackServer.CorsHeaders}Content-Length: 11\r\n\r\nhello world")); } [Theory] @@ -349,6 +362,7 @@ await LoopbackServer.CreateClientAndServerAsync(async uri => [InlineData(true, false)] [InlineData(false, true)] [InlineData(true, true)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54160", TestPlatforms.Browser)] public async Task GetAsync_IncompleteData_ThrowsHttpRequestException(bool failDuringHeaders, bool getString) { if (IsWinHttpHandler) @@ -372,8 +386,8 @@ await LoopbackServer.CreateClientAndServerAsync(async uri => } }, server => failDuringHeaders ? - server.AcceptConnectionSendCustomResponseAndCloseAsync("HTTP/1.1 200 OK\r\nContent-Length: 5\r\n") : - server.AcceptConnectionSendCustomResponseAndCloseAsync("HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nhe")); + server.AcceptConnectionSendCustomResponseAndCloseAsync($"HTTP/1.1 200 OK\r\n{LoopbackServer.CorsHeaders}Content-Length: 5\r\n") : + server.AcceptConnectionSendCustomResponseAndCloseAsync($"HTTP/1.1 200 OK\r\n{LoopbackServer.CorsHeaders}Content-Length: 5\r\n\r\nhe")); } [Fact] @@ -396,10 +410,13 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => var request = new HttpRequestMessage(HttpMethod.Post, uri) { Content = new ByteArrayContent(contentArray), Version = UseVersion }; request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("text/plain")); - request.Headers.AcceptCharset.Add(new StringWithQualityHeaderValue("utf-8")); - request.Headers.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip")); - request.Headers.AcceptEncoding.Add(new StringWithQualityHeaderValue("deflate")); - request.Headers.AcceptLanguage.Add(new StringWithQualityHeaderValue("en-US")); + if (PlatformDetection.IsNotBrowser) + { + request.Headers.AcceptCharset.Add(new StringWithQualityHeaderValue("utf-8")); + request.Headers.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip")); + request.Headers.AcceptEncoding.Add(new StringWithQualityHeaderValue("deflate")); + request.Headers.AcceptLanguage.Add(new StringWithQualityHeaderValue("en-US")); + } request.Headers.Add("Accept-Datetime", "Thu, 31 May 2007 20:35:00 GMT"); request.Headers.Add("Access-Control-Request-Method", "GET"); request.Headers.Add("Access-Control-Request-Headers", "GET"); @@ -409,7 +426,10 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => request.Headers.Connection.Add("close"); request.Headers.Add("Cookie", "$Version=1; Skin=new"); request.Content.Headers.ContentLength = contentArray.Length; - request.Content.Headers.ContentMD5 = MD5.Create().ComputeHash(contentArray); + if (PlatformDetection.IsNotBrowser) + { + request.Content.Headers.ContentMD5 = MD5.Create().ComputeHash(contentArray); + } request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded"); request.Headers.Date = DateTimeOffset.Parse("Tue, 15 Nov 1994 08:12:31 GMT"); request.Headers.Expect.Add(new NameValueWithParametersHeaderValue("100-continue")); @@ -431,11 +451,14 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => request.Headers.TE.Add(new TransferCodingWithQualityHeaderValue("deflate")); request.Headers.Trailer.Add("MyTrailer"); request.Headers.TransferEncoding.Add(new TransferCodingHeaderValue("chunked")); - request.Headers.UserAgent.Add(new ProductInfoHeaderValue(new ProductHeaderValue("Mozilla", "5.0"))); - request.Headers.Upgrade.Add(new ProductHeaderValue("HTTPS", "1.3")); - request.Headers.Upgrade.Add(new ProductHeaderValue("IRC", "6.9")); - request.Headers.Upgrade.Add(new ProductHeaderValue("RTA", "x11")); - request.Headers.Upgrade.Add(new ProductHeaderValue("websocket")); + if (PlatformDetection.IsNotBrowser) + { + request.Headers.UserAgent.Add(new ProductInfoHeaderValue(new ProductHeaderValue("Mozilla", "5.0"))); + request.Headers.Upgrade.Add(new ProductHeaderValue("HTTPS", "1.3")); + request.Headers.Upgrade.Add(new ProductHeaderValue("IRC", "6.9")); + request.Headers.Upgrade.Add(new ProductHeaderValue("RTA", "x11")); + request.Headers.Upgrade.Add(new ProductHeaderValue("websocket")); + } request.Headers.Via.Add(new ViaHeaderValue("1.0", "fred")); request.Headers.Via.Add(new ViaHeaderValue("1.1", "example.com", null, "(Apache/1.1)")); request.Headers.Warning.Add(new WarningHeaderValue(199, "-", "\"Miscellaneous warning\"")); @@ -471,18 +494,31 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => Assert.Equal(content, Encoding.ASCII.GetString(requestData.Body)); - Assert.Equal("utf-8", requestData.GetSingleHeaderValue("Accept-Charset")); - Assert.Equal("gzip, deflate", requestData.GetSingleHeaderValue("Accept-Encoding")); - Assert.Equal("en-US", requestData.GetSingleHeaderValue("Accept-Language")); - Assert.Equal("Thu, 31 May 2007 20:35:00 GMT", requestData.GetSingleHeaderValue("Accept-Datetime")); - Assert.Equal("GET", requestData.GetSingleHeaderValue("Access-Control-Request-Method")); - Assert.Equal("GET", requestData.GetSingleHeaderValue("Access-Control-Request-Headers")); + if (PlatformDetection.IsNotBrowser) + { + Assert.Equal("utf-8", requestData.GetSingleHeaderValue("Accept-Charset")); + Assert.Equal("gzip, deflate", requestData.GetSingleHeaderValue("Accept-Encoding")); + Assert.Equal("en-US", requestData.GetSingleHeaderValue("Accept-Language")); + Assert.Equal("Thu, 31 May 2007 20:35:00 GMT", requestData.GetSingleHeaderValue("Accept-Datetime")); + Assert.Equal("GET", requestData.GetSingleHeaderValue("Access-Control-Request-Method")); + Assert.Equal("GET", requestData.GetSingleHeaderValue("Access-Control-Request-Headers")); + } Assert.Equal("12", requestData.GetSingleHeaderValue("Age")); Assert.Equal("Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==", requestData.GetSingleHeaderValue("Authorization")); Assert.Equal("no-cache", requestData.GetSingleHeaderValue("Cache-Control")); - Assert.Equal("$Version=1; Skin=new", requestData.GetSingleHeaderValue("Cookie")); - Assert.Equal("Tue, 15 Nov 1994 08:12:31 GMT", requestData.GetSingleHeaderValue("Date")); - Assert.Equal("100-continue", requestData.GetSingleHeaderValue("Expect")); + if (PlatformDetection.IsNotBrowser) + { + Assert.Equal("$Version=1; Skin=new", requestData.GetSingleHeaderValue("Cookie")); + Assert.Equal("Tue, 15 Nov 1994 08:12:31 GMT", requestData.GetSingleHeaderValue("Date")); + Assert.Equal("100-continue", requestData.GetSingleHeaderValue("Expect")); + Assert.Equal("http://www.example-social-network.com", requestData.GetSingleHeaderValue("Origin")); + Assert.Equal("Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==", requestData.GetSingleHeaderValue("Proxy-Authorization")); + Assert.Equal("Mozilla/5.0", requestData.GetSingleHeaderValue("User-Agent")); + Assert.Equal("http://en.wikipedia.org/wiki/Main_Page", requestData.GetSingleHeaderValue("Referer")); + Assert.Equal("MyTrailer", requestData.GetSingleHeaderValue("Trailer")); + Assert.Equal("1.0 fred, 1.1 example.com (Apache/1.1)", requestData.GetSingleHeaderValue("Via")); + Assert.Equal("1 (Do Not Track Enabled)", requestData.GetSingleHeaderValue("DNT")); + } Assert.Equal("for=192.0.2.60;proto=http;by=203.0.113.43", requestData.GetSingleHeaderValue("Forwarded")); Assert.Equal("User Name ", requestData.GetSingleHeaderValue("From")); Assert.Equal("\"37060cd8c284d8af7ad3082f209582d\"", requestData.GetSingleHeaderValue("If-Match")); @@ -491,17 +527,10 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => Assert.Equal("Wed, 21 Oct 2015 07:28:00 GMT", requestData.GetSingleHeaderValue("If-Range")); Assert.Equal("Sat, 29 Oct 1994 19:43:31 GMT", requestData.GetSingleHeaderValue("If-Unmodified-Since")); Assert.Equal("10", requestData.GetSingleHeaderValue("Max-Forwards")); - Assert.Equal("http://www.example-social-network.com", requestData.GetSingleHeaderValue("Origin")); Assert.Equal("no-cache", requestData.GetSingleHeaderValue("Pragma")); - Assert.Equal("Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==", requestData.GetSingleHeaderValue("Proxy-Authorization")); Assert.Equal("bytes=500-999", requestData.GetSingleHeaderValue("Range")); - Assert.Equal("http://en.wikipedia.org/wiki/Main_Page", requestData.GetSingleHeaderValue("Referer")); - Assert.Equal("MyTrailer", requestData.GetSingleHeaderValue("Trailer")); - Assert.Equal("Mozilla/5.0", requestData.GetSingleHeaderValue("User-Agent")); - Assert.Equal("1.0 fred, 1.1 example.com (Apache/1.1)", requestData.GetSingleHeaderValue("Via")); Assert.Equal("199 - \"Miscellaneous warning\"", requestData.GetSingleHeaderValue("Warning")); Assert.Equal("XMLHttpRequest", requestData.GetSingleHeaderValue("X-Requested-With")); - Assert.Equal("1 (Do Not Track Enabled)", requestData.GetSingleHeaderValue("DNT")); Assert.Equal("client1, proxy1, proxy2", requestData.GetSingleHeaderValue("X-Forwarded-For")); Assert.Equal("en.wikipedia.org:8080", requestData.GetSingleHeaderValue("X-Forwarded-Host")); Assert.Equal("https", requestData.GetSingleHeaderValue("X-Forwarded-Proto")); @@ -527,7 +556,7 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => Assert.Equal(0, requestData.GetHeaderValueCount("Connection")); Assert.Equal(0, requestData.GetHeaderValueCount("Transfer-Encoding")); } - else + else if (PlatformDetection.IsNotBrowser) { // Verify HTTP/1.x headers Assert.Equal("close", requestData.GetSingleHeaderValue("Connection"), StringComparer.OrdinalIgnoreCase); // NetFxHandler uses "Close" vs "close" @@ -565,7 +594,10 @@ await LoopbackServer.CreateClientAndServerAsync(async uri => { Assert.Equal("1.1", resp.Version.ToString()); Assert.Equal(HttpStatusCode.OK, resp.StatusCode); - Assert.Contains("*", resp.Headers.GetValues("Access-Control-Allow-Origin")); + if (PlatformDetection.IsNotBrowser) + { + Assert.Contains("*", resp.Headers.GetValues("Access-Control-Allow-Origin")); + } Assert.Contains("text/example;charset=utf-8", resp.Headers.GetValues("Accept-Patch")); Assert.Contains("bytes", resp.Headers.AcceptRanges); Assert.Equal(TimeSpan.FromSeconds(12), resp.Headers.Age.GetValueOrDefault()); @@ -581,7 +613,10 @@ await LoopbackServer.CreateClientAndServerAsync(async uri => Assert.Contains("gzip", resp.Content.Headers.ContentEncoding); Assert.Contains("da", resp.Content.Headers.ContentLanguage); Assert.Equal(new Uri("/index.htm", UriKind.Relative), resp.Content.Headers.ContentLocation); - Assert.Equal(Convert.FromBase64String("Q2hlY2sgSW50ZWdyaXR5IQ=="), resp.Content.Headers.ContentMD5); + if (PlatformDetection.IsNotBrowser) + { + Assert.Equal(Convert.FromBase64String("Q2hlY2sgSW50ZWdyaXR5IQ=="), resp.Content.Headers.ContentMD5); + } Assert.Equal("bytes", resp.Content.Headers.ContentRange.Unit); Assert.Equal(21010, resp.Content.Headers.ContentRange.From.GetValueOrDefault()); Assert.Equal(47021, resp.Content.Headers.ContentRange.To.GetValueOrDefault()); @@ -600,7 +635,11 @@ await LoopbackServer.CreateClientAndServerAsync(async uri => Assert.Contains("max-age=2592000; pin-sha256=\"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\"", resp.Headers.GetValues("Public-Key-Pins")); Assert.Equal(TimeSpan.FromSeconds(120), resp.Headers.RetryAfter.Delta.GetValueOrDefault()); Assert.Contains(new ProductInfoHeaderValue("Apache", "2.4.1"), resp.Headers.Server); - Assert.Contains("UserID=JohnDoe; Max-Age=3600; Version=1", resp.Headers.GetValues("Set-Cookie")); + + if (PlatformDetection.IsNotBrowser) + { + Assert.Contains("UserID=JohnDoe; Max-Age=3600; Version=1", resp.Headers.GetValues("Set-Cookie")); + } Assert.Contains("max-age=16070400; includeSubDomains", resp.Headers.GetValues("Strict-Transport-Security")); Assert.Contains("Max-Forwards", resp.Headers.Trailer); Assert.Contains("?", resp.Headers.GetValues("Tk")); @@ -627,6 +666,7 @@ await LoopbackServer.CreateClientAndServerAsync(async uri => }, server => server.AcceptConnectionSendCustomResponseAndCloseAsync( $"HTTP/1.1 200 OK{newline}" + $"Access-Control-Allow-Origin:{fold} *{newline}" + + $"Access-Control-Expose-Headers:{fold} *{newline}" + $"Accept-Patch:{fold} text/example;charset=utf-8{newline}" + $"Accept-Ranges:{fold} bytes{newline}" + $"Age: {fold}12{newline}" + @@ -682,6 +722,7 @@ await LoopbackServer.CreateClientAndServerAsync(async uri => } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54160", TestPlatforms.Browser)] public async Task GetAsync_NonTraditionalChunkSizes_Accepted() { if (LoopbackServerFactory.Version >= HttpVersion20.Value) @@ -700,6 +741,7 @@ await TestHelper.WhenAllCompletedOrAnyFailed( server.AcceptConnectionSendCustomResponseAndCloseAsync( "HTTP/1.1 200 OK\r\n" + "Connection: close\r\n" + + LoopbackServer.CorsHeaders + "Transfer-Encoding: chunked\r\n" + "\r\n" + "4 \r\n" + // whitespace after size @@ -733,6 +775,7 @@ await TestHelper.WhenAllCompletedOrAnyFailed( [InlineData("xyz")] // non-hex [InlineData("7gibberish")] // valid size then gibberish [InlineData("7\v\f")] // unacceptable whitespace + [ActiveIssue("https://github.com/dotnet/runtime/issues/54160", TestPlatforms.Browser)] public async Task GetAsync_InvalidChunkSize_ThrowsHttpRequestException(string chunkSize) { if (UseVersion != HttpVersion.Version11) @@ -745,6 +788,7 @@ await LoopbackServer.CreateServerAsync(async (server, url) => using (HttpClient client = CreateHttpClient()) { string partialResponse = "HTTP/1.1 200 OK\r\n" + + LoopbackServer.CorsHeaders + "Transfer-Encoding: chunked\r\n" + "\r\n" + $"{chunkSize}\r\n"; @@ -764,6 +808,7 @@ await LoopbackServer.CreateServerAsync(async (server, url) => } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54160", TestPlatforms.Browser)] public async Task GetAsync_InvalidChunkTerminator_ThrowsHttpRequestException() { if (UseVersion != HttpVersion.Version11) @@ -780,6 +825,7 @@ await LoopbackServer.CreateClientAndServerAsync(async url => }, server => server.AcceptConnectionSendCustomResponseAndCloseAsync( "HTTP/1.1 200 OK\r\n" + "Connection: close\r\n" + + LoopbackServer.CorsHeaders + "Transfer-Encoding: chunked\r\n" + "\r\n" + "5\r\n" + @@ -791,6 +837,7 @@ await LoopbackServer.CreateClientAndServerAsync(async url => } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54160", TestPlatforms.Browser)] public async Task GetAsync_InfiniteChunkSize_ThrowsHttpRequestException() { if (UseVersion != HttpVersion.Version11) @@ -808,7 +855,7 @@ await LoopbackServer.CreateServerAsync(async (server, url) => var tcs = new TaskCompletionSource(); Task serverTask = server.AcceptConnectionAsync(async connection => { - await connection.ReadRequestHeaderAndSendCustomResponseAsync("HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"); + await connection.ReadRequestHeaderAndSendCustomResponseAsync("HTTP/1.1 200 OK\r\n" + LoopbackServer.CorsHeaders + "Transfer-Encoding: chunked\r\n\r\n"); try { while (!cts.IsCancellationRequested) // infinite to make sure implementation doesn't OOM @@ -828,6 +875,7 @@ await LoopbackServer.CreateServerAsync(async (server, url) => } [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "CORS is required on Browser")] public async Task SendAsync_TransferEncodingSetButNoRequestContent_Throws() { var req = new HttpRequestMessage(HttpMethod.Post, "http://bing.com") { Version = UseVersion }; @@ -841,6 +889,7 @@ public async Task SendAsync_TransferEncodingSetButNoRequestContent_Throws() [OuterLoop("Slow response")] [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "This is blocking forever on Browser, there is infinite default timeout")] public async Task SendAsync_ReadFromSlowStreamingServer_PartialDataReturned() { if (UseVersion != HttpVersion.Version11) @@ -856,9 +905,14 @@ await LoopbackServer.CreateServerAsync(async (server, url) => await server.AcceptConnectionAsync(async connection => { - await connection.ReadRequestHeaderAndSendCustomResponseAsync( + HttpRequestData requestData = await connection.ReadRequestDataAsync(); +#if TARGET_BROWSER + await connection.HandleCORSPreFlight(requestData); +#endif + await connection.WriteStringAsync( "HTTP/1.1 200 OK\r\n" + $"Date: {DateTimeOffset.UtcNow:R}\r\n" + + LoopbackServer.CorsHeaders + "Content-Length: 16000\r\n" + "\r\n" + "less than 16000 bytes"); @@ -882,6 +936,7 @@ await connection.ReadRequestHeaderAndSendCustomResponseAsync( [InlineData(true)] [InlineData(false)] [InlineData(null)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54159", TestPlatforms.Browser)] public async Task ReadAsStreamAsync_HandlerProducesWellBehavedResponseStream(bool? chunked) { if (IsWinHttpHandler && UseVersion >= HttpVersion20.Value) @@ -1047,6 +1102,7 @@ await server.AcceptConnectionAsync(async connection => } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54159", TestPlatforms.Browser)] public async Task ReadAsStreamAsync_EmptyResponseBody_HandlerProducesWellBehavedResponseStream() { if (IsWinHttpHandler && UseVersion >= HttpVersion20.Value) @@ -1211,6 +1267,7 @@ await LoopbackServerFactory.CreateServerAsync(async (server3, url3) => [Theory] [InlineData(99)] [InlineData(1000)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54160", TestPlatforms.Browser)] public async Task GetAsync_StatusCodeOutOfRange_ExpectedException(int statusCode) { if (UseVersion == HttpVersion30) @@ -1227,6 +1284,7 @@ await LoopbackServer.CreateServerAsync(async (server, url) => await server.AcceptConnectionSendCustomResponseAndCloseAsync( $"HTTP/1.1 {statusCode}\r\n" + $"Date: {DateTimeOffset.UtcNow:R}\r\n" + + LoopbackServer.CorsHeaders + "Connection: close\r\n" + "\r\n"); @@ -1255,6 +1313,7 @@ public async Task GetAsync_UnicodeHostName_SuccessStatusCodeInResponse() #region Post Methods Tests [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/53876", TestPlatforms.Browser)] public async Task GetAsync_ExpectContinueTrue_NoContent_StillSendsHeader() { if (IsWinHttpHandler && UseVersion >= HttpVersion20.Value) @@ -1306,6 +1365,7 @@ public static IEnumerable Interim1xxStatusCode() [Theory] [MemberData(nameof(Interim1xxStatusCode))] + [SkipOnPlatform(TestPlatforms.Browser, "CookieContainer is not supported on Browser")] public async Task SendAsync_1xxResponsesWithHeaders_InterimResponsesHeadersIgnored(HttpStatusCode responseStatusCode) { if (IsWinHttpHandler && UseVersion >= HttpVersion20.Value) @@ -1480,6 +1540,7 @@ await server.AcceptConnectionAsync(async connection => } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54160", TestPlatforms.Browser)] public async Task SendAsync_Expect100Continue_RequestBodyFails_ThrowsContentException() { if (IsWinHttpHandler) @@ -1576,6 +1637,7 @@ await server.AcceptConnectionAsync(async connection => } [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "Switching protocol is not supported on Browser")] public async Task SendAsync_101SwitchingProtocolsResponse_Success() { // WinHttpHandler and CurlHandler will hang, waiting for additional response. @@ -1616,6 +1678,7 @@ await connection.ReadRequestHeaderAndSendCustomResponseAsync( [Theory] [InlineData(false)] [InlineData(true)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54160", TestPlatforms.Browser)] public async Task PostAsync_ThrowFromContentCopy_RequestFails(bool syncFailure) { if (UseVersion == HttpVersion30) @@ -1629,7 +1692,7 @@ await LoopbackServer.CreateServerAsync(async (server, uri) => Task responseTask = server.AcceptConnectionAsync(async connection => { var buffer = new byte[1000]; - while (await connection.Socket.ReceiveAsync(new ArraySegment(buffer, 0, buffer.Length), SocketFlags.None) != 0); + while (await connection.ReadAsync(new Memory(buffer), 0, buffer.Length) != 0) ; }); using (HttpClient client = CreateHttpClient()) @@ -1651,6 +1714,7 @@ await LoopbackServer.CreateServerAsync(async (server, uri) => [Theory] [InlineData(HttpStatusCode.MethodNotAllowed, "Custom description")] [InlineData(HttpStatusCode.MethodNotAllowed, "")] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54163", TestPlatforms.Browser)] public async Task GetAsync_CallMethod_ExpectedStatusLine(HttpStatusCode statusCode, string reasonPhrase) { if (LoopbackServerFactory.Version >= HttpVersion20.Value) @@ -1668,13 +1732,14 @@ await LoopbackServer.CreateClientAndServerAsync(async uri => Assert.Equal(reasonPhrase, response.ReasonPhrase); } }, server => server.AcceptConnectionSendCustomResponseAndCloseAsync( - $"HTTP/1.1 {(int)statusCode} {reasonPhrase}\r\nContent-Length: 0\r\n\r\n")); + $"HTTP/1.1 {(int)statusCode} {reasonPhrase}\r\n{LoopbackServer.CorsHeaders}Content-Length: 0\r\n\r\n")); } #endregion #region Version tests + [SkipOnPlatform(TestPlatforms.Browser, "Version is not supported on Browser")] [Fact] public async Task SendAsync_RequestVersion10_ServerReceivesVersion10Request() { @@ -1688,6 +1753,7 @@ public async Task SendAsync_RequestVersion10_ServerReceivesVersion10Request() Assert.Equal(new Version(1, 0), receivedRequestVersion); } + [SkipOnPlatform(TestPlatforms.Browser, "Version is not supported on Browser")] [Fact] public async Task SendAsync_RequestVersion11_ServerReceivesVersion11Request() { @@ -1695,6 +1761,7 @@ public async Task SendAsync_RequestVersion11_ServerReceivesVersion11Request() Assert.Equal(new Version(1, 1), receivedRequestVersion); } + [SkipOnPlatform(TestPlatforms.Browser, "Version is not supported on Browser")] [Fact] public async Task SendAsync_RequestVersionNotSpecified_ServerReceivesVersion11Request() { diff --git a/src/libraries/Common/tests/System/Net/Http/LoopbackServer.cs b/src/libraries/Common/tests/System/Net/Http/LoopbackServer.cs index b44ae2d7f6803..5685e79cdb602 100644 --- a/src/libraries/Common/tests/System/Net/Http/LoopbackServer.cs +++ b/src/libraries/Common/tests/System/Net/Http/LoopbackServer.cs @@ -11,6 +11,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using System.Net.WebSockets; using Xunit; namespace System.Net.Test.Common @@ -20,55 +21,83 @@ public sealed partial class LoopbackServer : GenericLoopbackServer, IDisposable private static readonly byte[] s_newLineBytes = new byte[] { (byte)'\r', (byte)'\n' }; private static readonly byte[] s_colonSpaceBytes = new byte[] { (byte)':', (byte)' ' }; + private SocketWrapper _socketWrapper; +#if TARGET_BROWSER + private ClientWebSocket _listenSocket; +#else private Socket _listenSocket; +#endif private Options _options; private Uri _uri; public LoopbackServer(Options options = null) { _options = options ??= new Options(); + } + +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + public async Task ListenAsync() +#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously + { try { - _listenSocket = new Socket(options.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp); - _listenSocket.Bind(new IPEndPoint(options.Address, 0)); - _listenSocket.Listen(options.ListenBacklog); + IPEndPoint localEndPoint; +#if TARGET_BROWSER + _listenSocket = new ClientWebSocket(); - var localEndPoint = (IPEndPoint)_listenSocket.LocalEndPoint; - string host = options.Address.AddressFamily == AddressFamily.InterNetworkV6 ? + await _listenSocket.ConnectAsync(Configuration.Http.RemoteLoopServer, CancellationToken.None); + + byte[] buffer = new byte[128 * 1024]; + var message = Encoding.ASCII.GetBytes($"{_options.ListenBacklog},{_options.Address}"); + await _listenSocket.SendAsync(message, WebSocketMessageType.Binary, true, CancellationToken.None); + var first = await _listenSocket.ReceiveAsync(buffer, CancellationToken.None); + localEndPoint = IPEndPoint.Parse(Encoding.ASCII.GetString(buffer, 0, first.Count)); +#else + _listenSocket = new Socket(_options.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + _listenSocket.Bind(new IPEndPoint(_options.Address, 0)); + _listenSocket.Listen(_options.ListenBacklog); + localEndPoint = (IPEndPoint)_listenSocket.LocalEndPoint; +#endif + + string host = _options.Address.AddressFamily == AddressFamily.InterNetworkV6 ? $"[{localEndPoint.Address}]" : localEndPoint.Address.ToString(); - string scheme = options.UseSsl ? "https" : "http"; - if (options.WebSocketEndpoint) + string scheme = _options.UseSsl ? "https" : "http"; + if (_options.WebSocketEndpoint) { - scheme = options.UseSsl ? "wss" : "ws"; + scheme = _options.UseSsl ? "wss" : "ws"; } _uri = new Uri($"{scheme}://{host}:{localEndPoint.Port}/"); + _socketWrapper = new SocketWrapper(_listenSocket); } catch { _listenSocket?.Dispose(); + _socketWrapper?.Dispose(); throw; } } public override void Dispose() { - if (_listenSocket != null) + _listenSocket = null; + if (_socketWrapper != null) { - _listenSocket.Dispose(); - _listenSocket = null; + _socketWrapper.Dispose(); + _socketWrapper = null; } } - public Socket ListenSocket => _listenSocket; + public SocketWrapper ListenSocket => _socketWrapper; public override Uri Address => _uri; public static async Task CreateServerAsync(Func funcAsync, Options options = null) { using (var server = new LoopbackServer(options)) { + await server.ListenAsync(); await funcAsync(server).ConfigureAwait(false); } } @@ -96,23 +125,31 @@ public override async Task EstablishGenericConnection public async Task EstablishConnectionAsync() { - Socket s = await _listenSocket.AcceptAsync().ConfigureAwait(false); + SocketWrapper closableWrapper = null; try { + Stream stream = null; +#if TARGET_BROWSER + closableWrapper = new SocketWrapper(_listenSocket); + stream = new WebSocketStream(_listenSocket, ownsSocket: true); +#else + var socket = await _listenSocket.AcceptAsync().ConfigureAwait(false); + closableWrapper = new SocketWrapper(socket); + try { - s.NoDelay = true; + socket.NoDelay = true; } // OSX can throw if socket is in weird state during close or cancellation catch (SocketException ex) when (ex.SocketErrorCode == SocketError.InvalidArgument && PlatformDetection.IsOSXLike) { } - Stream stream = new NetworkStream(s, ownsSocket: false); - - return await Connection.CreateAsync(s, stream, _options); + stream = new NetworkStream(socket, ownsSocket: false); +#endif + return await Connection.CreateAsync(closableWrapper, stream, _options).ConfigureAwait(false); } catch (Exception) { - s.Close(); + closableWrapper?.Close(); throw; } } @@ -332,11 +369,19 @@ public static string GetHttpResponse(HttpStatusCode statusCode = HttpStatusCode. public static string GetHttpResponseHeaders(HttpStatusCode statusCode = HttpStatusCode.OK, string additionalHeaders = null, string content = null, bool connectionClose = false) => GetHttpResponseHeaders(statusCode, additionalHeaders, content == null ? 0 : content.Length, connectionClose); + public static string CorsHeaders = PlatformDetection.IsBrowser + ? "Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETE\r\n" + + "Access-Control-Expose-Headers: *\r\n" + + "Access-Control-Allow-Headers: *\r\n" + + "Access-Control-Allow-Origin: *\r\n" + : ""; + public static string GetHttpResponseHeaders(HttpStatusCode statusCode = HttpStatusCode.OK, string additionalHeaders = null, int contentLength = 0, bool connectionClose = false) => $"HTTP/1.1 {(int)statusCode} {GetStatusDescription(statusCode)}\r\n" + (connectionClose ? "Connection: close\r\n" : "") + $"Date: {DateTimeOffset.UtcNow:R}\r\n" + $"Content-Length: {contentLength}\r\n" + + CorsHeaders + additionalHeaders + "\r\n"; @@ -345,6 +390,7 @@ public static string GetSingleChunkHttpResponse(HttpStatusCode statusCode = Http (connectionClose ? "Connection: close\r\n" : "") + $"Date: {DateTimeOffset.UtcNow:R}\r\n" + "Transfer-Encoding: chunked\r\n" + + CorsHeaders + additionalHeaders + "\r\n" + (string.IsNullOrEmpty(content) ? "" : @@ -358,6 +404,7 @@ public static string GetBytePerChunkHttpResponse(HttpStatusCode statusCode = Htt (connectionClose ? "Connection: close\r\n" : "") + $"Date: {DateTimeOffset.UtcNow:R}\r\n" + "Transfer-Encoding: chunked\r\n" + + CorsHeaders + additionalHeaders + "\r\n" + (string.IsNullOrEmpty(content) ? "" : string.Concat(content.Select(c => $"1\r\n{c}\r\n"))) + @@ -368,6 +415,7 @@ public static string GetConnectionCloseResponse(HttpStatusCode statusCode = Http $"HTTP/1.1 {(int)statusCode} {GetStatusDescription(statusCode)}\r\n" + "Connection: close\r\n" + $"Date: {DateTimeOffset.UtcNow:R}\r\n" + + CorsHeaders + additionalHeaders + "\r\n" + content; @@ -395,7 +443,7 @@ public Options() public sealed class Connection : GenericLoopbackConnection { private const int BufferSize = 4000; - private Socket _socket; + private SocketWrapper _socket; private Stream _stream; private byte[] _readBuffer; private int _readStart; @@ -403,7 +451,7 @@ public sealed class Connection : GenericLoopbackConnection private int _contentLength = 0; private bool _bodyRead = false; - public Connection(Socket socket, Stream stream) + public Connection(SocketWrapper socket, Stream stream) { _socket = socket; _stream = stream; @@ -413,10 +461,10 @@ public Connection(Socket socket, Stream stream) _readEnd = 0; } - public Socket Socket => _socket; + public SocketWrapper Socket => _socket; public Stream Stream => _stream; - public static async Task CreateAsync(Socket socket, Stream stream, Options httpOptions) + public static async Task CreateAsync(SocketWrapper socket, Stream stream, Options httpOptions) { if (httpOptions.UseSsl) { @@ -706,6 +754,11 @@ public async Task> ReadRequestHeaderAndSendCustomResponseAsync(byte public async Task> ReadRequestHeaderAndSendResponseAsync(HttpStatusCode statusCode = HttpStatusCode.OK, string additionalHeaders = null, string content = null) { List lines = await ReadRequestHeaderAsync().ConfigureAwait(false); + +#if TARGET_BROWSER + lines = await HandleCORSPreFlight(lines); +#endif + await SendResponseAsync(statusCode, additionalHeaders, content).ConfigureAwait(false); return lines; } @@ -850,6 +903,12 @@ public override async Task SendResponseAsync(HttpStatusCode? statusCode = HttpSt } } + if (PlatformDetection.IsBrowser) + { + byte[] corsBytes = Encoding.ASCII.GetBytes(CorsHeaders); + headerBytes.Write(corsBytes, 0, corsBytes.Length); + } + bool endHeaders = content != null || isFinal; if (statusCode != null) { @@ -890,6 +949,7 @@ public override async Task SendResponseHeadersAsync(HttpStatusCode statusCode = headerString = headerString + $"{headerData.Name}: {headerData.Value}\r\n"; } } + headerString += CorsHeaders; headerString = GetHttpResponseHeaders(statusCode, headerString, 0, connectionClose: true); @@ -901,10 +961,52 @@ public override async Task SendResponseBodyAsync(byte[] body, bool isFinal = tru await SendResponseAsync(body).ConfigureAwait(false); } + public async Task HandleCORSPreFlight(HttpRequestData requestData) + { + if (PlatformDetection.IsBrowser && requestData.Method == "OPTIONS" && requestData.Headers.Any(h => h.Name.StartsWith("Access-Control-Request-Method"))) + { + // handle CORS pre-flight + await SendResponseAsync(HttpStatusCode.OK).ConfigureAwait(false); + + // reset state + _bodyRead = false; + _contentLength = 0; + _readStart = 0; + _readEnd = 0; + + // wait for real request + return await ReadRequestDataAsync().ConfigureAwait(false); + } + return requestData; + } + + public async Task> HandleCORSPreFlight(List lines) + { + if (PlatformDetection.IsBrowser && lines[0].Contains("OPTIONS") && lines.Any(h => h.StartsWith("Access-Control-Request-Method"))) + { + // handle CORS pre-flight + await SendResponseAsync(HttpStatusCode.OK).ConfigureAwait(false); + + // reset state + _bodyRead = false; + _contentLength = 0; + _readStart = 0; + _readEnd = 0; + + // wait for real request + return await ReadRequestHeaderAsync().ConfigureAwait(false); + } + return lines; + } + public override async Task HandleRequestAsync(HttpStatusCode statusCode = HttpStatusCode.OK, IList headers = null, string content = "") { HttpRequestData requestData = await ReadRequestDataAsync().ConfigureAwait(false); +#if TARGET_BROWSER + requestData = await HandleCORSPreFlight(requestData); +#endif + // For historical reasons, we added Date and "Connection: close" (to improve test reliability) bool hasDate = false; List newHeaders = new List(); @@ -970,7 +1072,9 @@ public sealed class Http11LoopbackServerFactory : LoopbackServerFactory public override GenericLoopbackServer CreateServer(GenericLoopbackOptions options = null) { - return new LoopbackServer(CreateOptions(options)); + var loopbackServer = new LoopbackServer(CreateOptions(options)); + Task.WaitAll(loopbackServer.ListenAsync()); + return loopbackServer; } public override Task CreateServerAsync(Func funcAsync, int millisecondsTimeout = 60_000, GenericLoopbackOptions options = null) @@ -978,7 +1082,7 @@ public override Task CreateServerAsync(Func fu return LoopbackServer.CreateServerAsync((server, uri) => funcAsync(server, uri), options: CreateOptions(options)); } - public override async Task CreateConnectionAsync(Socket socket, Stream stream, GenericLoopbackOptions options = null) + public override async Task CreateConnectionAsync(SocketWrapper socket, Stream stream, GenericLoopbackOptions options = null) { return await LoopbackServer.Connection.CreateAsync(socket, stream, CreateOptions(options)); } diff --git a/src/libraries/Common/tests/System/Net/Http/ResponseStreamTest.cs b/src/libraries/Common/tests/System/Net/Http/ResponseStreamTest.cs index 1a6cc656e9257..f6cc44f67e7c9 100644 --- a/src/libraries/Common/tests/System/Net/Http/ResponseStreamTest.cs +++ b/src/libraries/Common/tests/System/Net/Http/ResponseStreamTest.cs @@ -228,11 +228,11 @@ await client.GetAsync(remoteServer.EchoUri, HttpCompletionOption.ResponseHeaders } } #if NETCOREAPP - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] [Theory] [InlineData(TransferType.ContentLength, TransferError.ContentLengthTooLarge)] [InlineData(TransferType.Chunked, TransferError.MissingChunkTerminator)] [InlineData(TransferType.Chunked, TransferError.ChunkSizeTooLarge)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54160", TestPlatforms.Browser)] public async Task ReadAsStreamAsync_InvalidServerResponse_ThrowsIOException( TransferType transferType, TransferError transferError) @@ -243,7 +243,6 @@ await StartTransferTypeAndErrorServer(transferType, transferError, async uri => }); } - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] [Theory] [InlineData(TransferType.None, TransferError.None)] [InlineData(TransferType.ContentLength, TransferError.None)] @@ -258,7 +257,6 @@ await StartTransferTypeAndErrorServer(transferType, transferError, async uri => }); } - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] [Theory] [InlineData(TransferType.None, TransferError.None)] [InlineData(TransferType.ContentLength, TransferError.None)] @@ -327,6 +325,7 @@ public static Task StartTransferTypeAndErrorServer( // Write response header await connection.WriteStringAsync("HTTP/1.1 200 OK\r\n").ConfigureAwait(false); await connection.WriteStringAsync($"Date: {DateTimeOffset.UtcNow:R}\r\n").ConfigureAwait(false); + await connection.WriteStringAsync(LoopbackServer.CorsHeaders).ConfigureAwait(false); await connection.WriteStringAsync("Content-Type: text/plain\r\n").ConfigureAwait(false); if (!string.IsNullOrEmpty(transferHeader)) { diff --git a/src/libraries/Common/tests/System/Net/Prerequisites/LocalEchoServer.props b/src/libraries/Common/tests/System/Net/Prerequisites/LocalEchoServer.props index 97bdbf298b842..aeed390ba536b 100644 --- a/src/libraries/Common/tests/System/Net/Prerequisites/LocalEchoServer.props +++ b/src/libraries/Common/tests/System/Net/Prerequisites/LocalEchoServer.props @@ -9,9 +9,14 @@ <_TestEchoMiddleware Condition="'$(ContinuousIntegrationBuild)' == 'true' and '$(OS)' != 'Windows_NT'">$HELIX_CORRELATION_PAYLOAD/xharness/TestEchoMiddleware <_TestEchoMiddleware Condition="'$(ContinuousIntegrationBuild)' != 'true'">$(ArtifactsDir)bin/NetCoreServer/$(NetCoreAppCurrent)-$(Configuration) + <_RemoteLoopMiddleware Condition="'$(ContinuousIntegrationBuild)' == 'true' and '$(OS)' == 'Windows_NT'">%HELIX_CORRELATION_PAYLOAD%/xharness/RemoteLoopMiddleware + <_RemoteLoopMiddleware Condition="'$(ContinuousIntegrationBuild)' == 'true' and '$(OS)' != 'Windows_NT'">$HELIX_CORRELATION_PAYLOAD/xharness/RemoteLoopMiddleware + <_RemoteLoopMiddleware Condition="'$(ContinuousIntegrationBuild)' != 'true'">$(ArtifactsDir)bin/RemoteLoopServer/$(NetCoreAppCurrent)-$(Configuration) + $(WasmXHarnessArgs) --web-server-use-cors --web-server-use-https - $(WasmXHarnessArgs) --set-web-server-http-env=DOTNET_TEST_WEBSOCKETHOST,DOTNET_TEST_HTTPHOST + $(WasmXHarnessArgs) --set-web-server-http-env=DOTNET_TEST_WEBSOCKETHOST,DOTNET_TEST_HTTPHOST,DOTNET_TEST_REMOTE_LOOP_HOST $(WasmXHarnessArgs) --set-web-server-https-env=DOTNET_TEST_SECUREWEBSOCKETHOST,DOTNET_TEST_SECUREHTTPHOST,DOTNET_TEST_HTTP2HOST + $(WasmXHarnessArgs) --web-server-middleware=$(_RemoteLoopMiddleware)/RemoteLoopServer.dll,GenericHandler $(WasmXHarnessArgs) --web-server-middleware=$(_TestEchoMiddleware)/NetCoreServer.dll,GenericHandler diff --git a/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer.sln b/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer.sln deleted file mode 100644 index 8e5d44de5cd87..0000000000000 --- a/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.28307.438 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetCoreServer", "NetCoreServer\NetCoreServer.csproj", "{86E9A13D-9F4A-45DE-B0BB-CBB6A6533867}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {86E9A13D-9F4A-45DE-B0BB-CBB6A6533867}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {86E9A13D-9F4A-45DE-B0BB-CBB6A6533867}.Debug|Any CPU.Build.0 = Debug|Any CPU - {86E9A13D-9F4A-45DE-B0BB-CBB6A6533867}.Release|Any CPU.ActiveCfg = Release|Any CPU - {86E9A13D-9F4A-45DE-B0BB-CBB6A6533867}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {2F9A0637-452E-4FB9-9403-CB52944982DA} - EndGlobalSection -EndGlobal diff --git a/src/libraries/Common/tests/System/Net/Prerequisites/NetTestServers.sln b/src/libraries/Common/tests/System/Net/Prerequisites/NetTestServers.sln new file mode 100644 index 0000000000000..4075b414bebf7 --- /dev/null +++ b/src/libraries/Common/tests/System/Net/Prerequisites/NetTestServers.sln @@ -0,0 +1,30 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31229.75 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RemoteLoopServer", "RemoteLoopServer\RemoteLoopServer.csproj", "{86E9A13D-9F4A-45DE-B0BB-CBB6A6533868}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCoreServer", "NetCoreServer\NetCoreServer.csproj", "{2BB687CC-3F0C-43A9-8F38-140E91892EB0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {86E9A13D-9F4A-45DE-B0BB-CBB6A6533868}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {86E9A13D-9F4A-45DE-B0BB-CBB6A6533868}.Debug|Any CPU.Build.0 = Debug|Any CPU + {86E9A13D-9F4A-45DE-B0BB-CBB6A6533868}.Release|Any CPU.ActiveCfg = Release|Any CPU + {86E9A13D-9F4A-45DE-B0BB-CBB6A6533868}.Release|Any CPU.Build.0 = Release|Any CPU + {2BB687CC-3F0C-43A9-8F38-140E91892EB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2BB687CC-3F0C-43A9-8F38-140E91892EB0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2BB687CC-3F0C-43A9-8F38-140E91892EB0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2BB687CC-3F0C-43A9-8F38-140E91892EB0}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {2F9A0637-452E-4FB9-9403-CB52944982DA} + EndGlobalSection +EndGlobal diff --git a/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/GenericHandler.cs b/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/GenericHandler.cs new file mode 100644 index 0000000000000..3f01530103892 --- /dev/null +++ b/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/GenericHandler.cs @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; +using Microsoft.Extensions.Logging; + +namespace RemoteLoopServer +{ + public class GenericHandler + { + RequestDelegate _next; + ILogger _logger; + public GenericHandler(RequestDelegate next, ILogger logger) + { + this._next = next; + this._logger = logger; + } + + public async Task Invoke(HttpContext context) + { + PathString path = context.Request.Path; + if (path.Equals(new PathString("/RemoteLoop"))) + { + await RemoteLoopHandler.InvokeAsync(context, _logger); + return; + } + + await _next(context); + } + } + + public static class GenericHandlerExtensions + { + public static IApplicationBuilder UseGenericHandler(this IApplicationBuilder builder) + { + return builder.UseMiddleware(); + } + + public static void SetStatusDescription(this HttpResponse response, string description) + { + response.HttpContext.Features.Get().ReasonPhrase = description; + } + } +} diff --git a/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/Handlers/RemoteLoopHandler.cs b/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/Handlers/RemoteLoopHandler.cs new file mode 100644 index 0000000000000..4f83c67b0b30f --- /dev/null +++ b/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/Handlers/RemoteLoopHandler.cs @@ -0,0 +1,161 @@ +// 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.Net; +using System.Net.Sockets; +using System.Net.WebSockets; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; + +namespace RemoteLoopServer +{ + public partial class RemoteLoopHandler + { + public static async Task InvokeAsync(HttpContext context, ILogger logger) + { + try + { + if (!context.WebSockets.IsWebSocketRequest) + { + context.Response.StatusCode = 400; + context.Response.ContentType = "text/plain"; + await context.Response.WriteAsync("Not a websocket request"); + + return; + } + + using (WebSocket socket = await context.WebSockets.AcceptWebSocketAsync()) + { + await ProcessWebSocketRequest(context, socket, logger); + } + + } + catch (Exception ex) + { + logger.LogError("RemoteLoopHandler failed", ex); + } + } + + private static async Task ProcessWebSocketRequest(HttpContext context, WebSocket control, ILogger logger) + { + byte[] controlBuffer = new byte[128 * 1024]; + byte[] testedBuffer = new byte[128 * 1024]; + Socket listenSocket = null; + Socket tested = null; + CancellationTokenSource cts = new CancellationTokenSource(); + try + { + WebSocketReceiveResult first = await control.ReceiveAsync(controlBuffer, cts.Token).ConfigureAwait(false); + if (first.Count <= 0 || first.MessageType != WebSocketMessageType.Binary || control.State != WebSocketState.Open) + { + throw new Exception("Unexpected close"); + } + + // parse setup request + var message = Encoding.ASCII.GetString(controlBuffer, 0, first.Count); + var split = message.Split(','); + var listenBacklog = int.Parse(split[0]); + var address = IPAddress.Parse(split[1]); + + listenSocket = new Socket(address.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + listenSocket.Bind(new IPEndPoint(address, 0)); + listenSocket.Listen(listenBacklog); + EndPoint endPoint = listenSocket.LocalEndPoint; + + // respond with what we have done + await control.SendAsync(Encoding.ASCII.GetBytes(endPoint.ToString()), WebSocketMessageType.Binary, true, cts.Token).ConfigureAwait(false); + + // wait for the tested client to connect + tested = await listenSocket.AcceptAsync().ConfigureAwait(false); + + // now we are connected, pump messages + bool close = false; + + Task testedNext = tested.ReceiveAsync(new Memory(testedBuffer), SocketFlags.None, cts.Token).AsTask(); + Task controlNext = control.ReceiveAsync(controlBuffer, cts.Token); + while (!close) + { + // wait for either message + await Task.WhenAny(testedNext, controlNext).ConfigureAwait(false); + + if (testedNext.IsCompleted) + { + if (testedNext.IsCanceled || testedNext.IsFaulted) + { + close = true; + } + else + { + if (!tested.Connected) + { + close = true; + } + if (testedNext.Result > 0) + { + var slice = new ArraySegment(testedBuffer, 0, testedNext.Result); + await control.SendAsync(slice, WebSocketMessageType.Binary, true, cts.Token).ConfigureAwait(false); + } + if (!close) + { + testedNext = tested.ReceiveAsync(new Memory(testedBuffer), SocketFlags.None, cts.Token).AsTask(); + } + } + } + if (controlNext.IsCompleted) + { + if (controlNext.IsCanceled || controlNext.IsFaulted) + { + close = true; + } + else + { + if (controlNext.Result.MessageType == WebSocketMessageType.Close) + { + close = true; + } + if (controlNext.Result.Count > 0) + { + var slice = new ArraySegment(controlBuffer, 0, controlNext.Result.Count); + await tested.SendAsync(slice, SocketFlags.None, cts.Token).ConfigureAwait(false); + } + if (!close) + { + controlNext = control.ReceiveAsync(new ArraySegment(controlBuffer), cts.Token); + } + } + } + } + if (tested.Connected) + { + tested.Disconnect(false); + } + if (control.State == WebSocketState.Open || control.State == WebSocketState.Connecting || control.State == WebSocketState.None) + { + try + { + await control.CloseAsync(WebSocketCloseStatus.NormalClosure, "closing remoteLoop", cts.Token).ConfigureAwait(false); + } + catch (WebSocketException ex) + { + logger.LogWarning("ProcessWebSocketRequest closing failed", ex); + } + } + cts.Cancel(); + } + catch (Exception ex) + { + logger.LogError("ProcessWebSocketRequest failed", ex); + } + finally + { + tested?.Dispose(); + control?.Dispose(); + } + } + } +} diff --git a/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/Program.cs b/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/Program.cs new file mode 100644 index 0000000000000..f18d257efb967 --- /dev/null +++ b/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/Program.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 Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; + +namespace RemoteLoopServer +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/RemoteLoopServer.csproj b/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/RemoteLoopServer.csproj new file mode 100644 index 0000000000000..ba8a85a059c54 --- /dev/null +++ b/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/RemoteLoopServer.csproj @@ -0,0 +1,15 @@ + + + + $(AspNetCoreAppCurrent) + InProcess + Exe + + + + + + + + + diff --git a/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/Startup.cs b/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/Startup.cs new file mode 100644 index 0000000000000..bbf7b7bb79e65 --- /dev/null +++ b/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/Startup.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 Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; + +namespace RemoteLoopServer +{ + public class Startup + { + public void ConfigureServices(IServiceCollection services) + { + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + app.UseWebSockets(); + app.UseGenericHandler(); + } + } +} diff --git a/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/appsettings.Development.json b/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/appsettings.Development.json new file mode 100644 index 0000000000000..e203e9407e74a --- /dev/null +++ b/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } +} diff --git a/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/appsettings.json b/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/appsettings.json new file mode 100644 index 0000000000000..def9159a7d940 --- /dev/null +++ b/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/appsettings.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/src/libraries/Common/tests/System/Net/WebSockets/WebSocketStream.cs b/src/libraries/Common/tests/System/Net/WebSockets/WebSocketStream.cs new file mode 100644 index 0000000000000..322f5d2a00cb0 --- /dev/null +++ b/src/libraries/Common/tests/System/Net/WebSockets/WebSocketStream.cs @@ -0,0 +1,285 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Threading.Tasks; +using System.IO; +using System.Threading; + +namespace System.Net.WebSockets +{ + public class WebSocketStream : Stream + { + // Used by the class to hold the underlying socket the stream uses. + private readonly WebSocket _streamSocket; + + // Whether the stream should dispose of the socket when the stream is disposed + private readonly bool _ownsSocket; + + // Used by the class to indicate that the stream is m_Readable. + private bool _readable; + + // Used by the class to indicate that the stream is writable. + private bool _writeable; + + // Whether Dispose has been called. 0 == false, 1 == true + private int _disposed; + + public WebSocketStream(WebSocket socket) + : this(socket, FileAccess.ReadWrite, ownsSocket: false) + { + } + + public WebSocketStream(WebSocket socket, bool ownsSocket) + : this(socket, FileAccess.ReadWrite, ownsSocket) + { + } + + public WebSocketStream(WebSocket socket, FileAccess access) + : this(socket, access, ownsSocket: false) + { + } + + public WebSocketStream(WebSocket socket, FileAccess access, bool ownsSocket) + { + if (socket == null) + { + throw new ArgumentNullException(nameof(socket)); + } + if (socket.State != WebSocketState.Open) + { + throw new IOException("The operation is not allowed on non-connected sockets."); + } + + _streamSocket = socket; + _ownsSocket = ownsSocket; + + switch (access) + { + case FileAccess.Read: + _readable = true; + break; + case FileAccess.Write: + _writeable = true; + break; + case FileAccess.ReadWrite: + default: // assume FileAccess.ReadWrite + _readable = true; + _writeable = true; + break; + } + } + + public WebSocket Socket => _streamSocket; + + protected bool Readable + { + get { return _readable; } + set { _readable = value; } + } + + protected bool Writeable + { + get { return _writeable; } + set { _writeable = value; } + } + + public override bool CanRead => _readable; + public override bool CanSeek => false; + public override bool CanWrite => _writeable; + public override bool CanTimeout => true; + public override long Length => throw new NotSupportedException("This stream does not support seek operations."); + + public override long Position + { + get + { + throw new NotSupportedException("This stream does not support seek operations."); + } + set + { + throw new NotSupportedException("This stream does not support seek operations."); + } + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException("This stream does not support seek operations."); + } + + public override int Read(byte[] buffer, int offset, int count) + { + throw new IOException("The operation is not allowed on a non-blocking Socket."); + } + + public override int Read(Span buffer) + { + throw new IOException("The operation is not allowed on a non-blocking Socket."); + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new IOException("The operation is not allowed on a non-blocking Socket."); + } + + public override void Write(ReadOnlySpan buffer) + { + throw new IOException("The operation is not allowed on a non-blocking Socket."); + } + + private int _closeTimeout = -1; + + public void Close(int timeout) + { + if (timeout < -1) + { + throw new ArgumentOutOfRangeException(nameof(timeout)); + } + _closeTimeout = timeout; + Dispose(); + } + + protected override void Dispose(bool disposing) + { + if (Interlocked.Exchange(ref _disposed, 1) != 0) + { + return; + } + + if (disposing) + { + _readable = false; + _writeable = false; + if (_ownsSocket) + { + if (_streamSocket != null && (_streamSocket.State == WebSocketState.Open || _streamSocket.State == WebSocketState.Connecting || _streamSocket.State == WebSocketState.None)) + { + try + { + var task = _streamSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "closing remoteLoop", CancellationToken.None); + Task.WaitAll(task); + } + catch (Exception) + { + } + finally + { + _streamSocket.Dispose(); + } + } + } + } + + base.Dispose(disposing); + } + + ~WebSocketStream() => Dispose(false); + + public async override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + ValidateBufferArguments(buffer, offset, count); + ThrowIfDisposed(); + if (!CanRead) + { + throw new InvalidOperationException("The stream does not support reading."); + } + + try + { + var res = await _streamSocket.ReceiveAsync(new Memory(buffer, offset, count), cancellationToken); + return res.Count; + } + catch (Exception exception) when (!(exception is OutOfMemoryException)) + { + throw WrapException("Unable to read data from the transport connection", exception); + } + } + + public async override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken) + { + bool canRead = CanRead; // Prevent race with Dispose. + ThrowIfDisposed(); + if (!canRead) + { + throw new InvalidOperationException("The stream does not support reading."); + } + + try + { + var res = await _streamSocket.ReceiveAsync(buffer, + cancellationToken); + return res.Count; + } + catch (Exception exception) when (!(exception is OutOfMemoryException)) + { + throw WrapException("Unable to read data from the transport connection", exception); + } + } + + public async override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + ValidateBufferArguments(buffer, offset, count); + ThrowIfDisposed(); + if (!CanWrite) + { + throw new InvalidOperationException("The stream does not support writing."); + } + + try + { + await _streamSocket.SendAsync(new ReadOnlyMemory(buffer, offset, count), WebSocketMessageType.Binary, true, cancellationToken); + } + catch (Exception exception) when (!(exception is OutOfMemoryException)) + { + throw WrapException("Unable to write data to the transport connection", exception); + } + } + + public async override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken) + { + bool canWrite = CanWrite; // Prevent race with Dispose. + ThrowIfDisposed(); + if (!canWrite) + { + throw new InvalidOperationException("The stream does not support writing."); + } + + try + { + await _streamSocket.SendAsync(buffer, WebSocketMessageType.Binary, true, cancellationToken); + } + catch (Exception exception) when (!(exception is OutOfMemoryException)) + { + throw WrapException("Unable to write data to the transport connection", exception); + } + } + + public override void Flush() + { + } + + public override Task FlushAsync(CancellationToken cancellationToken) + { + return Task.CompletedTask; + } + + public override void SetLength(long value) + { + throw new NotSupportedException("This stream does not support seek operations."); + } + + private void ThrowIfDisposed() + { + if (_disposed != 0) + { + ThrowObjectDisposedException(); + } + + void ThrowObjectDisposedException() => throw new ObjectDisposedException(GetType().FullName); + } + + private static IOException WrapException(string resourceFormatString, Exception innerException) + { + return new IOException(resourceFormatString, innerException); + } + } +} diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.AltSvc.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.AltSvc.cs index 76d80f21450af..6cb8bbc075c2b 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.AltSvc.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.AltSvc.cs @@ -41,7 +41,7 @@ public async Task AltSvc_Header_Upgrade_Success(Version fromVersion, bool overri using GenericLoopbackServer firstServer = fromVersion.Major switch { - 1 => new LoopbackServer(new LoopbackServer.Options { UseSsl = true }), + 1 => Http11LoopbackServerFactory.Singleton.CreateServer(new LoopbackServer.Options { UseSsl = true }), 2 => Http2LoopbackServer.CreateServer(), _ => throw new Exception("Unknown HTTP version.") }; diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Headers.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Headers.cs index 73a543ac12a21..7a1ceaf88c792 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Headers.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Headers.cs @@ -23,7 +23,6 @@ public HttpClientHandlerTest_Headers(ITestOutputHelper output) : base(output) { private sealed class DerivedHttpHeaders : HttpHeaders { } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task SendAsync_RequestWithSimpleHeader_ResponseReferencesUnmodifiedRequestHeaders() { const string HeaderKey = "some-header-123", HeaderValue = "this is the expected header value"; @@ -47,7 +46,7 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "User-Agent is not supported on Browser")] public async Task SendAsync_UserAgent_CorrectlyWritten() { string userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.18 Safari/537.36"; @@ -71,7 +70,6 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task SendAsync_LargeHeaders_CorrectlyWritten() { if (UseVersion == HttpVersion.Version30) @@ -107,7 +105,6 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task SendAsync_DefaultHeaders_CorrectlyWritten() { const string Version = "2017-04-17"; @@ -137,7 +134,7 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => [Theory] [InlineData("\u05D1\u05F1")] [InlineData("jp\u30A5")] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54160", TestPlatforms.Browser)] public async Task SendAsync_InvalidCharactersInHeader_Throw(string value) { await LoopbackServerFactory.CreateClientAndServerAsync(async uri => @@ -169,9 +166,14 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => [InlineData("Accept-CharSet", "text/plain, text/json", false)] // invalid format for header but added with TryAddWithoutValidation [InlineData("Content-Location", "", false)] // invalid format for header but added with TryAddWithoutValidation [InlineData("Max-Forwards", "NotAnInteger", false)] // invalid format for header but added with TryAddWithoutValidation - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task SendAsync_SpecialHeaderKeyOrValue_Success(string key, string value, bool parsable) { + if (PlatformDetection.IsBrowser && (key == "Content-Location" || key == "Date" || key == "Accept-CharSet")) + { + // https://fetch.spec.whatwg.org/#forbidden-header-name + return; + } + await LoopbackServerFactory.CreateClientAndServerAsync(async uri => { bool contentHeader = false; @@ -209,7 +211,6 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => [Theory] [InlineData("Content-Security-Policy", 4618)] [InlineData("RandomCustomHeader", 12345)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task GetAsync_LargeHeader_Success(string headerName, int headerValueLength) { var rand = new Random(42); @@ -234,7 +235,6 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task GetAsync_EmptyResponseHeader_Success() { IList headers = new HttpHeaderData[] { @@ -246,8 +246,12 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => { using (HttpClient client = CreateHttpClient()) { - HttpResponseMessage response = await client.GetAsync(uri).ConfigureAwait(false); - Assert.Equal(headers.Count, response.Headers.Count()); + HttpResponseMessage response = await client.GetAsync(uri).ConfigureAwait(false); + // browser sends more headers + if (PlatformDetection.IsNotBrowser) + { + Assert.Equal(headers.Count, response.Headers.Count()); + } Assert.NotNull(response.Headers.GetValues("x-empty")); } }, @@ -262,7 +266,6 @@ await server.AcceptConnectionAsync(async connection => } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task GetAsync_MissingExpires_ReturnNull() { await LoopbackServerFactory.CreateClientAndServerAsync(async uri => @@ -283,7 +286,6 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => [InlineData("Thu, 01 Dec 1994 16:00:00 GMT", true)] [InlineData("-1", false)] [InlineData("0", false)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task SendAsync_Expires_Success(string value, bool isValid) { await LoopbackServerFactory.CreateClientAndServerAsync(async uri => @@ -322,7 +324,6 @@ public void HeadersAdd_CustomExpires_Success(string value, bool isValid) [Theory] [InlineData("Accept-Encoding", "identity,gzip")] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task SendAsync_RequestHeaderInResponse_Success(string name, string value) { await LoopbackServerFactory.CreateClientAndServerAsync(async uri => @@ -400,7 +401,7 @@ public async Task SendAsync_GetWithInvalidHostHeader_ThrowsException() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54160", TestPlatforms.Browser)] public async Task SendAsync_WithZeroLengthHeaderName_Throws() { await LoopbackServerFactory.CreateClientAndServerAsync( @@ -443,7 +444,7 @@ private static readonly (string Name, Encoding ValueEncoding, string[] Values)[] }; [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Socket is not supported on Browser")] public async Task SendAsync_CustomRequestEncodingSelector_CanSendNonAsciiHeaderValues() { await LoopbackServerFactory.CreateClientAndServerAsync( @@ -498,7 +499,7 @@ await LoopbackServerFactory.CreateClientAndServerAsync( } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Socket is not supported on Browser")] public async Task SendAsync_CustomResponseEncodingSelector_CanReceiveNonAsciiHeaderValues() { await LoopbackServerFactory.CreateClientAndServerAsync( diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http1.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http1.cs index c7579cd114bb2..f0e58b32ca345 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http1.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http1.cs @@ -17,7 +17,6 @@ public class HttpClientHandlerTest_Http1 : HttpClientHandlerTestBase public HttpClientHandlerTest_Http1(ITestOutputHelper output) : base(output) { } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] public async Task SendAsync_HostHeader_First() { // RFC 7230 3.2.2. Field Order diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.RequestRetry.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.RequestRetry.cs index ced259294f8c1..4e2426b958e65 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.RequestRetry.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.RequestRetry.cs @@ -19,7 +19,6 @@ public abstract class HttpClientHandlerTest_RequestRetry : HttpClientHandlerTest public HttpClientHandlerTest_RequestRetry(ITestOutputHelper output) : base(output) { } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task GetAsyncOnNewConnection_RetryOnConnectionClosed_Success() { await LoopbackServer.CreateClientAndServerAsync(async url => diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientMiniStressTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientMiniStressTest.cs index 6d68042a73316..af3bdd764bc72 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientMiniStressTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientMiniStressTest.cs @@ -98,6 +98,7 @@ await server.AcceptConnectionAsync(async connection => } [Collection(nameof(HttpClientMiniStress))] + [SkipOnPlatform(TestPlatforms.Browser, "System.Net.Security is not supported on Browser")] public abstract class HttpClientMiniStress : HttpClientHandlerTestBase { public HttpClientMiniStress(ITestOutputHelper output) : base(output) { } diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs index 94d59d89b03ff..451bb20d58fa1 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs @@ -357,8 +357,6 @@ public async Task GetAsync_CustomException_Asynchronous_ThrowsException() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task GetStringAsync_Success() { string content = Guid.NewGuid().ToString(); @@ -378,8 +376,7 @@ await LoopbackServer.CreateClientAndServerAsync( } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54270", TestPlatforms.Browser)] public async Task GetStringAsync_CanBeCanceled_AlreadyCanceledCts() { var onClientFinished = new SemaphoreSlim(0, 1); @@ -404,8 +401,7 @@ await LoopbackServer.CreateClientAndServerAsync( } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54270", TestPlatforms.Browser)] public async Task GetStringAsync_CanBeCanceled() { var cts = new CancellationTokenSource(); @@ -440,7 +436,7 @@ await server.AcceptConnectionAsync(async connection => [InlineData(1, 0)] [InlineData(1, 1)] [InlineData(1, 2)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54270", TestPlatforms.Browser)] public async Task GetAsync_ContentCanBeCanceled(int getMode, int cancelMode) { // cancelMode: @@ -530,8 +526,6 @@ await LoopbackServerFactory.CreateClientAndServerAsync( } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task GetByteArrayAsync_Success() { string content = Guid.NewGuid().ToString(); @@ -552,8 +546,7 @@ await LoopbackServer.CreateClientAndServerAsync( } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54270", TestPlatforms.Browser)] public async Task GetByteArrayAsync_CanBeCanceled_AlreadyCanceledCts() { var onClientFinished = new SemaphoreSlim(0, 1); @@ -578,8 +571,7 @@ await LoopbackServer.CreateClientAndServerAsync( } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54270", TestPlatforms.Browser)] public async Task GetByteArrayAsync_CanBeCanceled() { var cts = new CancellationTokenSource(); @@ -607,8 +599,6 @@ await server.AcceptConnectionAsync(async connection => } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task GetStreamAsync_Success() { string content = Guid.NewGuid().ToString(); @@ -632,8 +622,7 @@ await LoopbackServer.CreateClientAndServerAsync( } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54270", TestPlatforms.Browser)] public async Task GetStreamAsync_CanBeCanceled_AlreadyCanceledCts() { var onClientFinished = new SemaphoreSlim(0, 1); @@ -658,8 +647,7 @@ await LoopbackServer.CreateClientAndServerAsync( } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54270", TestPlatforms.Browser)] public async Task GetStreamAsync_CanBeCanceled() { var cts = new CancellationTokenSource(); @@ -916,8 +904,7 @@ public void Send_SingleThread_Succeeds(HttpCompletionOption completionOption) [Theory] [InlineData(HttpCompletionOption.ResponseContentRead)] [InlineData(HttpCompletionOption.ResponseHeadersRead)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Synchronous Send is not supported on Browser")] public async Task Send_SingleThread_Loopback_Succeeds(HttpCompletionOption completionOption) { string content = "Test content"; @@ -970,7 +957,7 @@ await server.AcceptConnectionAsync(async connection => [Fact] [OuterLoop] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Synchronous Send is not supported on Browser")] public async Task Send_CancelledRequestContent_Throws() { CancellationTokenSource cts = new CancellationTokenSource(); @@ -1017,7 +1004,6 @@ await server.AcceptConnectionAsync(async connection => [Fact] [OuterLoop] [ActiveIssue("https://github.com/dotnet/runtime/issues/39056")] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task Send_TimeoutRequestContent_Throws() { await LoopbackServer.CreateClientAndServerAsync( @@ -1060,7 +1046,7 @@ await server.AcceptConnectionAsync(async connection => [Fact] [OuterLoop] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Synchronous Send is not supported on Browser")] public async Task Send_CancelledResponseContent_Throws() { string content = "Test content"; @@ -1110,12 +1096,13 @@ await server.AcceptConnectionAsync(async connection => [Fact] [OuterLoop] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] - public void Send_TimeoutResponseContent_Throws() + [SkipOnPlatform(TestPlatforms.Browser, "Synchronous Send is not supported on Browser")] + public async Task Send_TimeoutResponseContent_Throws() { const string Content = "Test content"; using var server = new LoopbackServer(); + await server.ListenAsync(); // Ignore all failures from the server. This includes being disposed of before ever accepting a connection, // which is possible if the client times out so quickly that it hasn't initiated a connection yet. @@ -1163,8 +1150,7 @@ public static IEnumerable VersionSelectionMemberData() [Theory] [MemberData(nameof(VersionSelectionMemberData))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Version is ignored on Browser")] public async Task SendAsync_CorrectVersionSelected_LoopbackServer(Version requestVersion, HttpVersionPolicy versionPolicy, Version serverVersion, bool useSsl, object expectedResult) { await HttpAgnosticLoopbackServer.CreateClientAndServerAsync( diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpContentTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpContentTest.cs index 214042a185040..b09b0d2dc1a38 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpContentTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpContentTest.cs @@ -613,8 +613,6 @@ public void Dispose_DisposedObjectThenAccessMembers_ThrowsObjectDisposedExceptio [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task ReadAsStringAsync_Buffered_IgnoresCancellationToken() { string content = Guid.NewGuid().ToString(); @@ -641,8 +639,6 @@ await LoopbackServer.CreateClientAndServerAsync( } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task ReadAsStringAsync_Unbuffered_CanBeCanceled_AlreadyCanceledCts() { await LoopbackServer.CreateClientAndServerAsync( @@ -670,8 +666,6 @@ await LoopbackServer.CreateClientAndServerAsync( } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task ReadAsStringAsync_Unbuffered_CanBeCanceled() { var cts = new CancellationTokenSource(); @@ -706,8 +700,6 @@ await server.AcceptConnectionAsync(async connection => } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task ReadAsByteArrayAsync_Buffered_IgnoresCancellationToken() { string content = Guid.NewGuid().ToString(); @@ -735,8 +727,6 @@ await LoopbackServer.CreateClientAndServerAsync( } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task ReadAsByteArrayAsync_Unbuffered_CanBeCanceled_AlreadyCanceledCts() { await LoopbackServer.CreateClientAndServerAsync( @@ -764,8 +754,6 @@ await LoopbackServer.CreateClientAndServerAsync( } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task ReadAsByteArrayAsync_Unbuffered_CanBeCanceled() { var cts = new CancellationTokenSource(); @@ -802,8 +790,6 @@ await server.AcceptConnectionAsync(async connection => [Theory] [InlineData(true)] [InlineData(false)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task ReadAsStreamAsync_Buffered_IgnoresCancellationToken(bool readStreamAsync) { string content = Guid.NewGuid().ToString(); @@ -835,10 +821,13 @@ await LoopbackServer.CreateClientAndServerAsync( [Theory] [InlineData(true)] [InlineData(false)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task ReadAsStreamAsync_Unbuffered_IgnoresCancellationToken(bool readStreamAsync) { + if(PlatformDetection.IsBrowser && !readStreamAsync) + { + // syncronous operations are not supported on Browser + return; + } string content = Guid.NewGuid().ToString(); await LoopbackServer.CreateClientAndServerAsync( diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpRequestMessageTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpRequestMessageTest.cs index 167d5a0504c96..18b7fa9fe47cc 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpRequestMessageTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpRequestMessageTest.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; +using System.Linq; using System.Net.Http.Headers; using System.Net.Test.Common; using System.Threading.Tasks; @@ -12,7 +13,6 @@ namespace System.Net.Http.Functional.Tests { - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] public class HttpRequestMessageTest : HttpClientHandlerTestBase { private readonly Version _expectedRequestMessageVersion = HttpVersion.Version11; @@ -240,8 +240,12 @@ await LoopbackServer.CreateServerAsync(async (server, uri) => Task requestTask = client.SendAsync(request); await server.AcceptConnectionAsync(async connection => { - List headers = await connection.ReadRequestHeaderAsync(); - Assert.DoesNotContain(headers, line => line.StartsWith("Content-length")); + var requestData = await connection.ReadRequestDataAsync().ConfigureAwait(false); +#if TARGET_BROWSER + requestData = await connection.HandleCORSPreFlight(requestData); +#endif + + Assert.DoesNotContain(requestData.Headers, line => line.Name.StartsWith("Content-length")); await connection.SendResponseAsync(); await requestTask; diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs index 0ac38b257277f..cf7edc9881413 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs @@ -170,19 +170,19 @@ public sealed class SocketsHttpHandler_HttpClientHandler_Decompression_Tests : H public SocketsHttpHandler_HttpClientHandler_Decompression_Tests(ITestOutputHelper output) : base(output) { } } - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [SkipOnPlatform(TestPlatforms.Browser, "Certificates are not supported on Browser")] public sealed class SocketsHttpHandler_HttpClientHandler_DangerousAcceptAllCertificatesValidator_Test : HttpClientHandler_DangerousAcceptAllCertificatesValidator_Test { public SocketsHttpHandler_HttpClientHandler_DangerousAcceptAllCertificatesValidator_Test(ITestOutputHelper output) : base(output) { } } - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [SkipOnPlatform(TestPlatforms.Browser, "Certificates are not supported on Browser")] public sealed class SocketsHttpHandler_HttpClientHandler_ClientCertificates_Test : HttpClientHandler_ClientCertificates_Test { public SocketsHttpHandler_HttpClientHandler_ClientCertificates_Test(ITestOutputHelper output) : base(output) { } } - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [SkipOnPlatform(TestPlatforms.Browser, "Proxy is not supported on Browser")] public sealed class SocketsHttpHandler_HttpClientHandler_DefaultProxyCredentials_Test : HttpClientHandler_DefaultProxyCredentials_Test { public SocketsHttpHandler_HttpClientHandler_DefaultProxyCredentials_Test(ITestOutputHelper output) : base(output) { } @@ -248,13 +248,13 @@ await server.AcceptConnectionAsync(async connection => } } - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [SkipOnPlatform(TestPlatforms.Browser, "Certificates are not supported on Browser")] public sealed class SocketsHttpHandler_HttpClientHandler_ServerCertificates_Test : HttpClientHandler_ServerCertificates_Test { public SocketsHttpHandler_HttpClientHandler_ServerCertificates_Test(ITestOutputHelper output) : base(output) { } } - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [SkipOnPlatform(TestPlatforms.Browser, "ResponseDrainTimeout is not supported on Browser")] public sealed class SocketsHttpHandler_HttpClientHandler_ResponseDrain_Test : HttpClientHandler_ResponseDrain_Test { protected override void SetResponseDrainTimeout(HttpClientHandler handler, TimeSpan time) @@ -560,13 +560,13 @@ public sealed class SocketsHttpHandler_ResponseStreamTest : ResponseStreamTest public SocketsHttpHandler_ResponseStreamTest(ITestOutputHelper output) : base(output) { } } - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/37669", TestPlatforms.Browser)] public sealed class SocketsHttpHandler_HttpClientHandler_SslProtocols_Test : HttpClientHandler_SslProtocols_Test { public SocketsHttpHandler_HttpClientHandler_SslProtocols_Test(ITestOutputHelper output) : base(output) { } } - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [SkipOnPlatform(TestPlatforms.Browser, "UseProxy not supported on Browser")] public sealed class SocketsHttpHandler_HttpClientHandler_Proxy_Test : HttpClientHandler_Proxy_Test { public SocketsHttpHandler_HttpClientHandler_Proxy_Test(ITestOutputHelper output) : base(output) { } @@ -588,8 +588,7 @@ protected static Frame MakeDataFrame(int streamId, byte[] data, bool endStream = new DataFrame(data, (endStream ? FrameFlags.EndStream : FrameFlags.None), 0, streamId); } - // System.Net.Sockets is not supported on this platform - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54156", TestPlatforms.Browser)] public class SocketsHttpHandler_Http1_TrailingHeaders_Test : SocketsHttpHandler_TrailingHeaders_Test { public SocketsHttpHandler_Http1_TrailingHeaders_Test(ITestOutputHelper output) : base(output) { } @@ -609,6 +608,7 @@ await TestHelper.WhenAllCompletedOrAnyFailed( getResponseTask, server.AcceptConnectionSendCustomResponseAndCloseAsync( "HTTP/1.1 200 OK\r\n" + + LoopbackServer.CorsHeaders + "Connection: close\r\n" + "Transfer-Encoding: chunked\r\n" + (includeTrailerHeader ? "Trailer: MyCoolTrailerHeader, Hello\r\n" : "") + @@ -662,6 +662,7 @@ await TestHelper.WhenAllCompletedOrAnyFailed( getResponseTask, server.AcceptConnectionSendCustomResponseAndCloseAsync( "HTTP/1.1 200 OK\r\n" + + LoopbackServer.CorsHeaders + "Connection: close\r\n" + "Transfer-Encoding: chunked\r\n" + "Trailer: MyCoolTrailerHeader\r\n" + @@ -751,6 +752,7 @@ await LoopbackServer.CreateClientAndServerAsync(async url => } }, server => server.AcceptConnectionSendCustomResponseAndCloseAsync( "HTTP/1.1 200 OK\r\n" + + LoopbackServer.CorsHeaders + "Connection: close\r\n" + "Transfer-Encoding: chunked\r\n" + $"Trailer: Set-Cookie, MyCoolTrailerHeader, {name}, Hello\r\n" + @@ -780,6 +782,7 @@ await TestHelper.WhenAllCompletedOrAnyFailed( server.AcceptConnectionSendCustomResponseAndCloseAsync( "HTTP/1.1 200 OK\r\n" + "Connection: close\r\n" + + LoopbackServer.CorsHeaders + "Transfer-Encoding: chunked\r\n" + "Trailer: MyCoolTrailerHeader\r\n" + "\r\n" + @@ -987,7 +990,6 @@ public sealed class SocketsHttpHandler_SchSendAuxRecordHttpTest : SchSendAuxReco public SocketsHttpHandler_SchSendAuxRecordHttpTest(ITestOutputHelper output) : base(output) { } } - [SkipOnPlatform(TestPlatforms.Browser, "Tests hang with chrome. To be investigated")] public sealed class SocketsHttpHandler_HttpClientHandlerTest : HttpClientHandlerTest { public SocketsHttpHandler_HttpClientHandlerTest(ITestOutputHelper output) : base(output) { } @@ -1017,19 +1019,19 @@ public sealed class SocketsHttpHandlerTest_RequestRetry : HttpClientHandlerTest_ public SocketsHttpHandlerTest_RequestRetry(ITestOutputHelper output) : base(output) { } } - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [SkipOnPlatform(TestPlatforms.Browser, "UseCookies is not supported on Browser")] public sealed class SocketsHttpHandlerTest_Cookies : HttpClientHandlerTest_Cookies { public SocketsHttpHandlerTest_Cookies(ITestOutputHelper output) : base(output) { } } - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [SkipOnPlatform(TestPlatforms.Browser, "UseCookies is not supported on Browser")] public sealed class SocketsHttpHandlerTest_Cookies_Http11 : HttpClientHandlerTest_Cookies_Http11 { public SocketsHttpHandlerTest_Cookies_Http11(ITestOutputHelper output) : base(output) { } } - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [SkipOnPlatform(TestPlatforms.Browser, "ConnectTimeout is not supported on Browser")] public sealed class SocketsHttpHandler_HttpClientHandler_Http11_Cancellation_Test : SocketsHttpHandler_Cancellation_Test { public SocketsHttpHandler_HttpClientHandler_Http11_Cancellation_Test(ITestOutputHelper output) : base(output) { } @@ -1130,15 +1132,13 @@ public void Expect100ContinueTimeout_SetAfterUse_Throws() } } - // BrowserHttpHandler.set_MaxResponseHeadersLength - Not supported on this platform - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [SkipOnPlatform(TestPlatforms.Browser, "MaxResponseHeadersLength is not supported on Browser")] public sealed class SocketsHttpHandler_HttpClientHandler_MaxResponseHeadersLength_Test : HttpClientHandler_MaxResponseHeadersLength_Test { public SocketsHttpHandler_HttpClientHandler_MaxResponseHeadersLength_Test(ITestOutputHelper output) : base(output) { } } - //System.Net.Sockets is not supported on this platform - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [SkipOnPlatform(TestPlatforms.Browser, "Socket is not supported on Browser")] public sealed class SocketsHttpHandler_HttpClientHandler_Authentication_Test : HttpClientHandler_Authentication_Test { public SocketsHttpHandler_HttpClientHandler_Authentication_Test(ITestOutputHelper output) : base(output) { } @@ -1158,7 +1158,7 @@ await LoopbackServer.CreateServerAsync(async (server, url) => { // We need to use ResponseHeadersRead here, otherwise we will hang trying to buffer the response body. Task getResponseTask = client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead); - await server.AcceptConnectionAsync(async connection => + await server.AcceptConnectionAsync(async (LoopbackServer.Connection connection) => { Task> serverTask = connection.ReadRequestHeaderAndSendCustomResponseAsync($"HTTP/1.1 101 Switching Protocols\r\nDate: {DateTimeOffset.UtcNow:R}\r\n\r\n"); @@ -1252,7 +1252,7 @@ await server.AcceptConnectionAsync(async connection => Task copyTask = clientStream.CopyToAsync(ms); string bigString = string.Concat(Enumerable.Repeat("abcdefghijklmnopqrstuvwxyz", 1000)); - Task lotsOfDataSent = connection.Socket.SendAsync(Encoding.ASCII.GetBytes(bigString), SocketFlags.None); + Task lotsOfDataSent = connection.SendResponseAsync(Encoding.ASCII.GetBytes(bigString)); connection.Socket.Shutdown(SocketShutdown.Send); await copyTask; await lotsOfDataSent; @@ -1270,8 +1270,7 @@ public sealed class SocketsHttpHandler_Connect_Test : HttpClientHandler_Connect_ public SocketsHttpHandler_Connect_Test(ITestOutputHelper output) : base(output) { } } - // System.Net.Sockets is not supported on this platform - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [SkipOnPlatform(TestPlatforms.Browser, "Socket is not supported on Browser")] public sealed class SocketsHttpHandler_HttpClientHandler_ConnectionPooling_Test : HttpClientHandlerTestBase { public SocketsHttpHandler_HttpClientHandler_ConnectionPooling_Test(ITestOutputHelper output) : base(output) { } @@ -1468,7 +1467,7 @@ await Http2LoopbackServerFactory.CreateServerAsync(async (server, url) => // Wait a small amount of time before making the second request, to give the first request time to timeout. await Task.Delay(100); // Grab reference to underlying socket and stream to make sure they are not disposed and closed. - (Socket socket, Stream stream) = connection.ResetNetwork(); + (SocketWrapper socket, Stream stream) = connection.ResetNetwork(); // Make second request and expect it to be served from a different connection. Task request2 = client.GetStringAsync(url); @@ -1997,8 +1996,7 @@ await Assert.ThrowsAnyAsync(() => } } - // System.Net.Sockets is not supported on this platform - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [SkipOnPlatform(TestPlatforms.Browser, "Headers.Location are not supported on Browser")] public sealed class SocketsHttpHandlerTest_LocationHeader { private static readonly byte[] s_redirectResponseBefore = Encoding.ASCII.GetBytes( @@ -2671,8 +2669,7 @@ public async Task ConnectCallback_ReturnsNull_ThrowsHttpRequestException(bool us private static bool PlatformSupportsUnixDomainSockets => Socket.OSSupportsUnixDomainSockets; } - // System.Net.Sockets is not supported on this platform - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [SkipOnPlatform(TestPlatforms.Browser, "Socket is not supported on Browser")] public sealed class SocketsHttpHandlerTest_ConnectCallback_Http11 : SocketsHttpHandlerTest_ConnectCallback { public SocketsHttpHandlerTest_ConnectCallback_Http11(ITestOutputHelper output) : base(output) { } diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocksProxyTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocksProxyTest.cs index 633f562c9491b..821794e9f82d5 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocksProxyTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocksProxyTest.cs @@ -113,14 +113,14 @@ public async Task TestExceptionalAsync(string scheme, string host, bool useAuth, } - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [SkipOnPlatform(TestPlatforms.Browser, "UseProxy not supported on Browser")] public sealed class SocksProxyTest_Http1_Async : SocksProxyTest { public SocksProxyTest_Http1_Async(ITestOutputHelper helper) : base(helper) { } protected override Version UseVersion => HttpVersion.Version11; } - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [SkipOnPlatform(TestPlatforms.Browser, "UseProxy not supported on Browser")] public sealed class SocksProxyTest_Http1_Sync : SocksProxyTest { public SocksProxyTest_Http1_Sync(ITestOutputHelper helper) : base(helper) { } @@ -128,7 +128,7 @@ public SocksProxyTest_Http1_Sync(ITestOutputHelper helper) : base(helper) { } protected override bool TestAsync => false; } - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [SkipOnPlatform(TestPlatforms.Browser, "UseProxy not supported on Browser")] public sealed class SocksProxyTest_Http2 : SocksProxyTest { public SocksProxyTest_Http2(ITestOutputHelper helper) : base(helper) { } diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj b/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj index 7c9b9d6d15738..672ce364f7061 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj @@ -13,12 +13,17 @@ WasmTestOnBrowser $(TestArchiveRoot)browseronly/ $(TestArchiveTestsRoot)$(OSPlatformConfig)/ + $(DefineConstants);TARGET_BROWSER + + + diff --git a/src/libraries/System.Net.WebSockets.Client/tests/CloseTest.cs b/src/libraries/System.Net.WebSockets.Client/tests/CloseTest.cs index 68773696bfcdb..a10e9e4639e71 100644 --- a/src/libraries/System.Net.WebSockets.Client/tests/CloseTest.cs +++ b/src/libraries/System.Net.WebSockets.Client/tests/CloseTest.cs @@ -374,7 +374,7 @@ public async Task CloseAsync_DuringConcurrentReceiveAsync_ExpectedStates(Uri ser [ConditionalFact(nameof(WebSocketsSupported))] [ActiveIssue("https://github.com/dotnet/runtime/issues/34690", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54153", TestPlatforms.Browser)] public async Task CloseAsync_CancelableEvenWhenPendingReceive_Throws() { var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); diff --git a/src/libraries/System.Net.WebSockets.Client/tests/ConnectTest.cs b/src/libraries/System.Net.WebSockets.Client/tests/ConnectTest.cs index 443166cafec66..0fb1314a6068e 100644 --- a/src/libraries/System.Net.WebSockets.Client/tests/ConnectTest.cs +++ b/src/libraries/System.Net.WebSockets.Client/tests/ConnectTest.cs @@ -58,7 +58,7 @@ public async Task EchoTextMessage_Success(Uri server) [OuterLoop("Uses external servers", typeof(PlatformDetection), nameof(PlatformDetection.LocalEchoServerIsNotAvailable))] [ConditionalTheory(nameof(WebSocketsSupported)), MemberData(nameof(EchoHeadersServers))] - [SkipOnPlatform(TestPlatforms.Browser, "CustomHeaders not supported on browser")] + [SkipOnPlatform(TestPlatforms.Browser, "SetRequestHeader not supported on browser")] public async Task ConnectAsync_AddCustomHeaders_Success(Uri server) { using (var cws = new ClientWebSocket()) @@ -96,7 +96,7 @@ public async Task ConnectAsync_AddCustomHeaders_Success(Uri server) [ConditionalFact(nameof(WebSocketsSupported))] [ActiveIssue("https://github.com/dotnet/runtime/issues/34690", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "SetRequestHeader not supported on browser")] public async Task ConnectAsync_AddHostHeader_Success() { string expectedHost = null; @@ -218,7 +218,7 @@ public async Task ConnectAsync_PassMultipleSubProtocols_ServerRequires_Connectio [ConditionalFact(nameof(WebSocketsSupported))] [ActiveIssue("https://github.com/dotnet/runtime/issues/34690", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "SetRequestHeader not supported on Browser")] public async Task ConnectAsync_NonStandardRequestHeaders_HeadersAddedWithoutValidation() { await LoopbackServer.CreateClientAndServerAsync(async uri => @@ -237,7 +237,7 @@ await LoopbackServer.CreateClientAndServerAsync(async uri => [OuterLoop("Uses external servers", typeof(PlatformDetection), nameof(PlatformDetection.LocalEchoServerIsNotAvailable))] [ConditionalTheory(nameof(WebSocketsSupported)), MemberData(nameof(EchoServers))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Proxy not supported on Browser")] public async Task ConnectAndCloseAsync_UseProxyServer_ExpectedClosedState(Uri server) { using (var cws = new ClientWebSocket()) @@ -282,7 +282,7 @@ public async Task ConnectAsync_CancellationRequestedInflightConnect_ThrowsOperat [ConditionalFact(nameof(WebSocketsSupported))] [ActiveIssue("https://github.com/dotnet/runtime/issues/34690", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54152", TestPlatforms.Browser)] public async Task ConnectAsync_CancellationRequestedAfterConnect_ThrowsOperationCanceledException() { var releaseServer = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); diff --git a/src/libraries/System.Net.WebSockets.Client/tests/DeflateTests.cs b/src/libraries/System.Net.WebSockets.Client/tests/DeflateTests.cs index 1f12d76743357..e0a0e1e59fd84 100644 --- a/src/libraries/System.Net.WebSockets.Client/tests/DeflateTests.cs +++ b/src/libraries/System.Net.WebSockets.Client/tests/DeflateTests.cs @@ -22,7 +22,6 @@ public DeflateTests(ITestOutputHelper output) : base(output) [ConditionalTheory(nameof(WebSocketsSupported))] [ActiveIssue("https://github.com/dotnet/runtime/issues/34690", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] [InlineData(15, true, 15, true, "permessage-deflate; client_max_window_bits")] [InlineData(14, true, 15, true, "permessage-deflate; client_max_window_bits=14")] [InlineData(15, true, 14, true, "permessage-deflate; client_max_window_bits; server_max_window_bits=14")] diff --git a/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs b/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs index d3a04bdbeef96..6266f01e8ede5 100644 --- a/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs +++ b/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs @@ -389,7 +389,7 @@ public async Task SendReceive_Concurrent_Success(Uri server) [OuterLoop("Uses external servers", typeof(PlatformDetection), nameof(PlatformDetection.LocalEchoServerIsNotAvailable))] [ConditionalFact(nameof(WebSocketsSupported))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54153", TestPlatforms.Browser)] public async Task SendReceive_ConnectionClosedPrematurely_ReceiveAsyncFailsAndWebSocketStateUpdated() { var options = new LoopbackServer.Options { WebSocketEndpoint = true }; diff --git a/src/libraries/System.Net.WebSockets.Client/tests/System.Net.WebSockets.Client.Tests.csproj b/src/libraries/System.Net.WebSockets.Client/tests/System.Net.WebSockets.Client.Tests.csproj index 22b43cc0edcf9..956ee4d414612 100644 --- a/src/libraries/System.Net.WebSockets.Client/tests/System.Net.WebSockets.Client.Tests.csproj +++ b/src/libraries/System.Net.WebSockets.Client/tests/System.Net.WebSockets.Client.Tests.csproj @@ -9,12 +9,17 @@ WasmTestOnBrowser $(TestArchiveRoot)browseronly/ $(TestArchiveTestsRoot)$(OSPlatformConfig)/ + $(DefineConstants);TARGET_BROWSER + + + diff --git a/src/libraries/sendtohelixhelp.proj b/src/libraries/sendtohelixhelp.proj index 574bee9c7dce5..25d7168bdc463 100644 --- a/src/libraries/sendtohelixhelp.proj +++ b/src/libraries/sendtohelixhelp.proj @@ -277,6 +277,7 @@ $([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'src', 'mono', 'wasm', 'emsdk')) $([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'src', 'mono', 'wasm', 'build')) $([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'bin', 'NetCoreServer', '$(NetCoreAppCurrent)-$(Configuration)')) + $([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'bin', 'RemoteLoopServer', '$(NetCoreAppCurrent)-$(Configuration)')) @@ -311,6 +312,7 @@ + From 183c4d100f68fb6c177a1fe71809d581aa25e47b Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Tue, 22 Jun 2021 09:44:28 -0400 Subject: [PATCH 056/107] Fix XML in Version.Details.xml --- eng/Version.Details.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 4177e280edf32..99fb28cb6e9bd 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -216,7 +216,7 @@ 25b814e010cd4796cedfbcce72a274c26928f496 - https://github.com/dotnet/runtime-assets + https://github.com/dotnet/runtime-assets 8d7b898b96cbdb868cac343e938173105287ed9e From 85aebc422ee12c13a15c94aedd5a2e7627e47db7 Mon Sep 17 00:00:00 2001 From: Andrew Au Date: Tue, 22 Jun 2021 09:00:43 -0700 Subject: [PATCH 057/107] Fix deadlock (#54426) --- src/coreclr/gc/gc.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 909e5580c72fb..f4fdf3177ef86 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -20587,6 +20587,7 @@ void gc_heap::garbage_collect (int n) update_collection_counts_for_no_gc(); #ifdef MULTIPLE_HEAPS + gc_start_event.Reset(); gc_t_join.restart(); #endif //MULTIPLE_HEAPS } From 2a61c25b26814431163b53e9f9f2e47a750c48e7 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 22 Jun 2021 09:32:53 -0700 Subject: [PATCH 058/107] Fail COM CoClass creation during construction instead of during jitting (#54508) --- src/coreclr/vm/ecall.cpp | 20 +++++++------------ src/coreclr/vm/ecall.h | 2 -- src/coreclr/vm/gchelpers.cpp | 12 ++++++++++- .../COM/NETClients/ComDisabled/Program.cs | 16 ++++++--------- src/tests/issues.targets | 3 --- 5 files changed, 24 insertions(+), 29 deletions(-) diff --git a/src/coreclr/vm/ecall.cpp b/src/coreclr/vm/ecall.cpp index 974556232f62e..c6d1b6d2f1edd 100644 --- a/src/coreclr/vm/ecall.cpp +++ b/src/coreclr/vm/ecall.cpp @@ -434,12 +434,6 @@ PCODE ECall::GetFCallImpl(MethodDesc * pMD, BOOL * pfSharedOrDynamicFCallImpl /* #endif // FEATURE_COMINTEROP ) { -#ifdef FEATURE_COMINTEROP - if (g_pBaseCOMObject == NULL) - { - COMPlusThrow(kPlatformNotSupportedException, IDS_EE_ERROR_COM); - } - if (pfSharedOrDynamicFCallImpl) *pfSharedOrDynamicFCallImpl = TRUE; @@ -448,9 +442,6 @@ PCODE ECall::GetFCallImpl(MethodDesc * pMD, BOOL * pfSharedOrDynamicFCallImpl /* // FCComCtor does not need to be in the fcall hashtable since it does not erect frame. return GetEEFuncEntryPoint(FCComCtor); -#else - COMPlusThrow(kPlatformNotSupportedException, IDS_EE_ERROR_COM); -#endif // FEATURE_COMINTEROP } if (!pMD->GetModule()->IsSystem()) @@ -571,9 +562,7 @@ BOOL ECall::IsSharedFCallImpl(PCODE pImpl) PCODE pNativeCode = pImpl; return -#ifdef FEATURE_COMINTEROP (pNativeCode == GetEEFuncEntryPoint(FCComCtor)) || -#endif (pNativeCode == GetEEFuncEntryPoint(COMDelegate::DelegateConstruct)); } @@ -619,7 +608,12 @@ BOOL ECall::CheckUnusedECalls(SetSHash& usedIDs) } -#if defined(FEATURE_COMINTEROP) && !defined(CROSSGEN_COMPILE) +#if !defined(CROSSGEN_COMPILE) +// This function is a stub implementation for the constructor of a ComImport class. +// The actual work to implement COM Activation (and built-in COM support checks) is done as part +// of the implementation of object allocation. As a result, the constructor itself has no extra +// work to do once the object has been allocated. As a result, we just provide a dummy implementation +// here since a constructor has to have an implementation. FCIMPL1(VOID, FCComCtor, LPVOID pV) { FCALL_CONTRACT; @@ -627,7 +621,7 @@ FCIMPL1(VOID, FCComCtor, LPVOID pV) FCUnique(0x34); } FCIMPLEND -#endif // FEATURE_COMINTEROP && !CROSSGEN_COMPILE +#endif // !CROSSGEN_COMPILE diff --git a/src/coreclr/vm/ecall.h b/src/coreclr/vm/ecall.h index 387bbc2e9ddf6..9b946ad198222 100644 --- a/src/coreclr/vm/ecall.h +++ b/src/coreclr/vm/ecall.h @@ -134,8 +134,6 @@ class ECall static LPVOID GetQCallImpl(MethodDesc * pMD); }; -#ifdef FEATURE_COMINTEROP extern "C" FCDECL1(VOID, FCComCtor, LPVOID pV); -#endif #endif // _ECALL_H_ diff --git a/src/coreclr/vm/gchelpers.cpp b/src/coreclr/vm/gchelpers.cpp index 74412a9e06d65..0cecfc624a744 100644 --- a/src/coreclr/vm/gchelpers.cpp +++ b/src/coreclr/vm/gchelpers.cpp @@ -930,15 +930,25 @@ OBJECTREF AllocateObject(MethodTable *pMT #ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION if (fHandleCom && pMT->IsComObjectType()) { + if (!g_pConfig->IsBuiltInCOMSupported()) + { + COMPlusThrow(kNotSupportedException, W("NotSupported_COM")); + } + // Create a instance of __ComObject here is not allowed as we don't know what COM object to create if (pMT == g_pBaseCOMObject) COMPlusThrow(kInvalidComObjectException, IDS_EE_NO_BACKING_CLASS_FACTORY); oref = OBJECTREF_TO_UNCHECKED_OBJECTREF(AllocateComObject_ForManaged(pMT)); } - else #endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION +#else // FEATURE_COMINTEROP + if (pMT->IsComObjectType()) + { + COMPlusThrow(kPlatformNotSupportedException, IDS_EE_ERROR_COM); + } #endif // FEATURE_COMINTEROP + else { GC_ALLOC_FLAGS flags = GC_ALLOC_NO_FLAGS; if (pMT->ContainsPointers()) diff --git a/src/tests/Interop/COM/NETClients/ComDisabled/Program.cs b/src/tests/Interop/COM/NETClients/ComDisabled/Program.cs index d7f6a83983a09..8f25b3dbcf870 100644 --- a/src/tests/Interop/COM/NETClients/ComDisabled/Program.cs +++ b/src/tests/Interop/COM/NETClients/ComDisabled/Program.cs @@ -20,22 +20,18 @@ static int Main(string[] doNotUse) try { - ActivateServer(); + var server = (Server.Contract.Servers.NumericTesting)new Server.Contract.Servers.NumericTestingClass(); } - catch (PlatformNotSupportedException ex) + catch (NotSupportedException) when (OperatingSystem.IsWindows()) + { + return 100; + } + catch (PlatformNotSupportedException) when (!OperatingSystem.IsWindows()) { return 100; } return 101; } - - // Mark as NoInlining to make sure the failure is observed while running Main, - // not while JITing Main and trying to resolve the target of the constructor call. - [MethodImpl(MethodImplOptions.NoInlining)] - private static void ActivateServer() - { - var server = (Server.Contract.Servers.NumericTesting)new Server.Contract.Servers.NumericTestingClass(); - } } } diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 25b94c7c75cb3..1daa9fda46d9e 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -123,9 +123,6 @@ https://github.com/dotnet/runtime/issues/48727 - - https://github.com/dotnet/runtime/issues/54379 - https://github.com/dotnet/runtime/issues/54316 From 5f778101c6867f1490889658cd27095cded236fe Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Tue, 22 Jun 2021 12:35:38 -0400 Subject: [PATCH 059/107] [wasm] Collect, and process satellite assemblies automatically (#53656) --- eng/testing/tests.wasm.targets | 19 +- src/mono/wasm/build/WasmApp.Native.targets | 4 +- src/mono/wasm/build/WasmApp.targets | 16 +- .../aot-tests/ProxyProjectForAOTOnHelix.proj | 8 +- src/tasks/WasmAppBuilder/WasmAppBuilder.cs | 7 + .../Wasm.Build.Tests/BuildTestBase.cs | 57 ++++-- .../InvariantGlobalizationTests.cs | 6 +- .../Wasm.Build.Tests/MainWithArgsTests.cs | 2 +- .../Wasm.Build.Tests/NativeBuildTests.cs | 4 +- .../Wasm.Build.Tests/RebuildTests.cs | 2 +- .../SatelliteAssembliesTests.cs | 179 ++++++++++++++++++ .../SharedBuildPerTestClassFixture.cs | 4 +- .../Wasm.Build.Tests/Wasm.Build.Tests.csproj | 5 +- .../Wasm.Build.Tests/WasmBuildAppTest.cs | 9 +- .../testassets/LibraryWithResources/Class1.cs | 11 ++ .../Directory.Build.props | 1 + .../Directory.Build.targets | 1 + .../LibraryWithResources.csproj | 10 + .../testassets/resx/words.es-ES.resx | 127 +++++++++++++ .../testassets/resx/words.ja-JP.resx | 127 +++++++++++++ .../BuildWasmApps/testassets/resx/words.resx | 127 +++++++++++++ 21 files changed, 672 insertions(+), 54 deletions(-) create mode 100644 src/tests/BuildWasmApps/Wasm.Build.Tests/SatelliteAssembliesTests.cs create mode 100644 src/tests/BuildWasmApps/testassets/LibraryWithResources/Class1.cs create mode 100644 src/tests/BuildWasmApps/testassets/LibraryWithResources/Directory.Build.props create mode 100644 src/tests/BuildWasmApps/testassets/LibraryWithResources/Directory.Build.targets create mode 100644 src/tests/BuildWasmApps/testassets/LibraryWithResources/LibraryWithResources.csproj create mode 100644 src/tests/BuildWasmApps/testassets/resx/words.es-ES.resx create mode 100644 src/tests/BuildWasmApps/testassets/resx/words.ja-JP.resx create mode 100644 src/tests/BuildWasmApps/testassets/resx/words.resx diff --git a/eng/testing/tests.wasm.targets b/eng/testing/tests.wasm.targets index 1e1c86695796e..de4432bee9f5e 100644 --- a/eng/testing/tests.wasm.targets +++ b/eng/testing/tests.wasm.targets @@ -69,8 +69,7 @@ - - + @@ -131,17 +130,17 @@ -1 - - - + + <_SatelliteAssemblies Include="$(PublishDir)*\*.resources.dll" /> + <_SatelliteAssemblies CultureName="$([System.IO.Directory]::GetParent('%(Identity)').Name)" /> + <_SatelliteAssemblies TargetPath="%(CultureName)\%(FileName)%(Extension)" /> - + + + + - <_CopyLocalPaths Include="@(PublishItemsOutputGroupOutputs)" diff --git a/src/mono/wasm/build/WasmApp.Native.targets b/src/mono/wasm/build/WasmApp.Native.targets index b16195a618af1..b6897fbd2b413 100644 --- a/src/mono/wasm/build/WasmApp.Native.targets +++ b/src/mono/wasm/build/WasmApp.Native.targets @@ -353,6 +353,7 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_ @(MonoAOTCompilerDefaultAotArguments, ';') @(MonoAOTCompilerDefaultProcessArguments, ';') + <_AOT_InternalForceInterpretAssemblies Include="@(_WasmAssembliesInternal->WithMetadataValue('_InternalForceInterpret', 'true'))" /> <_WasmAssembliesInternal Remove="@(_WasmAssembliesInternal)" /> @@ -422,9 +423,6 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_ <_WasmNativeFileForLinking Include="@(_BitcodeFile)" /> - - diff --git a/src/mono/wasm/build/WasmApp.targets b/src/mono/wasm/build/WasmApp.targets index b886f327d7162..034287436892b 100644 --- a/src/mono/wasm/build/WasmApp.targets +++ b/src/mono/wasm/build/WasmApp.targets @@ -61,7 +61,6 @@ Public items: - @(WasmExtraFilesToDeploy) - Files to copy to $(WasmAppDir). (relative path can be set via %(TargetPath) metadata) - - @(WasmSatelliteAssemblies) - @(WasmFilesToIncludeInFileSystem) - Files to include in the vfs - @(WasmNativeAsset) - Native files to be added to `NativeAssets` in the bundle. @@ -115,6 +114,13 @@ <_WasmAssembliesInternal Include="@(WasmAssembliesToBundle->Distinct())" /> + + <_WasmSatelliteAssemblies Include="@(_WasmAssembliesInternal)" /> + <_WasmSatelliteAssemblies Remove="@(_WasmSatelliteAssemblies)" Condition="!$([System.String]::Copy('%(Identity)').EndsWith('.resources.dll'))" /> + + <_WasmSatelliteAssemblies CultureName="$([System.IO.Directory]::GetParent('%(Identity)').Name)" /> + + <_WasmAssembliesInternal Remove="@(_WasmSatelliteAssemblies)" /> @@ -142,7 +148,7 @@ MainJS="$(WasmMainJSPath)" Assemblies="@(_WasmAssembliesInternal)" InvariantGlobalization="$(InvariantGlobalization)" - SatelliteAssemblies="@(WasmSatelliteAssemblies)" + SatelliteAssemblies="@(_WasmSatelliteAssemblies)" FilesToIncludeInFileSystem="@(WasmFilesToIncludeInFileSystem)" IcuDataFileName="$(WasmIcuDataFileName)" RemoteSources="@(WasmRemoteSources)" @@ -193,11 +199,5 @@ - - - diff --git a/src/mono/wasm/data/aot-tests/ProxyProjectForAOTOnHelix.proj b/src/mono/wasm/data/aot-tests/ProxyProjectForAOTOnHelix.proj index 0a0b915ad43ce..cdd5e95a4e210 100644 --- a/src/mono/wasm/data/aot-tests/ProxyProjectForAOTOnHelix.proj +++ b/src/mono/wasm/data/aot-tests/ProxyProjectForAOTOnHelix.proj @@ -33,17 +33,15 @@ - - - - + <_ExtraFiles Include="$(ExtraFilesPath)**\*" /> - <_SatelliteAssembliesForVFS Include="@(WasmSatelliteAssemblies)" /> + <_SatelliteAssembliesForVFS Include="$(OriginalPublishDir)**\*.resources.dll" /> + <_SatelliteAssembliesForVFS CultureName="$([System.IO.Directory]::GetParent('%(Identity)').Name)" /> <_SatelliteAssembliesForVFS TargetPath="%(CultureName)\%(FileName)%(Extension)" /> diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs index 49d984490a64b..660272e268aa4 100644 --- a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs +++ b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs @@ -189,6 +189,13 @@ public override bool Execute () { string culture = assembly.GetMetadata("CultureName") ?? string.Empty; string fullPath = assembly.GetMetadata("Identity"); + if (string.IsNullOrEmpty(culture)) + { + Log.LogWarning($"Missing CultureName metadata for satellite assembly {fullPath}"); + continue; + } + // FIXME: validate the culture? + string name = Path.GetFileName(fullPath); string directory = Path.Combine(AppDir, config.AssemblyRoot, culture); Directory.CreateDirectory(directory); diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs index 99e721dfa65f7..b87ff5c70bfc2 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs @@ -8,9 +8,11 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; +using System.Reflection; using System.Text; using Xunit; using Xunit.Abstractions; +using Xunit.Sdk; #nullable enable @@ -29,7 +31,6 @@ public abstract class BuildTestBase : IClassFixture test, string? buildDir=null, int expectedExitCode=0, string? args=null) + protected string RunAndTestWasmApp(BuildArgs buildArgs, + RunHost host, + string id, + Action? test=null, + string? buildDir = null, + int expectedExitCode = 0, + string? args = null, + Dictionary? envVars = null) { buildDir ??= _projectDir; - Dictionary? envVars = new(); + envVars ??= new(); envVars["XHARNESS_DISABLE_COLORED_OUTPUT"] = "true"; if (buildArgs.AOT) { @@ -180,6 +188,11 @@ protected void RunAndTestWasmApp(BuildArgs buildArgs, RunHost host, string id, A Assert.DoesNotContain("AOT: image 'System.Private.CoreLib' found.", output); Assert.DoesNotContain($"AOT: image '{buildArgs.ProjectName}' found.", output); } + + if (test != null) + test(output); + + return output; } protected static string RunWithXHarness(string testCommand, string testLogPath, string projectName, string bundleDir, @@ -209,6 +222,8 @@ protected static string RunWithXHarness(string testCommand, string testLogPath, args.Append($" --run {projectName}.dll"); args.Append($" {appArgs ?? string.Empty}"); + _testOutput.WriteLine(string.Empty); + _testOutput.WriteLine($"---------- Running with {testCommand} ---------"); var (exitCode, output) = RunProcess("dotnet", _testOutput, args: args.ToString(), workingDir: bundleDir, @@ -251,14 +266,21 @@ protected static void InitProjectDir(string dir) runtime-test.js ##EXTRA_PROPERTIES## + + ##EXTRA_ITEMS## + + ##INSERT_AT_END## "; - protected static BuildArgs GetBuildArgsWith(BuildArgs buildArgs, string? extraProperties=null, string projectTemplate=SimpleProjectTemplate) + protected static BuildArgs ExpandBuildArgs(BuildArgs buildArgs, string extraProperties="", string extraItems="", string insertAtEnd="", string projectTemplate=SimpleProjectTemplate) { if (buildArgs.AOT) - extraProperties = $"{extraProperties}\ntrue\n"; + extraProperties = $"{extraProperties}\ntrue\nfalse\n"; - string projectContents = projectTemplate.Replace("##EXTRA_PROPERTIES##", extraProperties ?? string.Empty); + string projectContents = projectTemplate + .Replace("##EXTRA_PROPERTIES##", extraProperties) + .Replace("##EXTRA_ITEMS##", extraItems) + .Replace("##INSERT_AT_END##", insertAtEnd); return buildArgs with { ProjectFileContents = projectContents }; } @@ -273,10 +295,10 @@ protected static BuildArgs GetBuildArgsWith(BuildArgs buildArgs, string? extraPr { if (useCache && _buildContext.TryGetBuildFor(buildArgs, out BuildProduct? product)) { - Console.WriteLine ($"Using existing build found at {product.BuildPath}, with build log at {product.LogFile}"); + Console.WriteLine ($"Using existing build found at {product.ProjectDir}, with build log at {product.LogFile}"); - Assert.True(product.Result, $"Found existing build at {product.BuildPath}, but it had failed. Check build log at {product.LogFile}"); - _projectDir = product.BuildPath; + Assert.True(product.Result, $"Found existing build at {product.ProjectDir}, but it had failed. Check build log at {product.LogFile}"); + _projectDir = product.ProjectDir; // use this test's id for the run logs _logPath = Path.Combine(s_logRoot, id); @@ -297,7 +319,6 @@ protected static BuildArgs GetBuildArgsWith(BuildArgs buildArgs, string? extraPr throw new Exception("_projectDir should be set, to use createProject=false"); } - StringBuilder sb = new(); sb.Append("publish"); sb.Append(s_defaultBuildArgs); @@ -305,6 +326,7 @@ protected static BuildArgs GetBuildArgsWith(BuildArgs buildArgs, string? extraPr sb.Append($" /p:Configuration={buildArgs.Config}"); string logFilePath = Path.Combine(_logPath, $"{buildArgs.ProjectName}.binlog"); + _testOutput.WriteLine($"-------- Building ---------"); _testOutput.WriteLine($"Binlog path: {logFilePath}"); sb.Append($" /bl:\"{logFilePath}\" /v:minimal /nologo"); if (buildArgs.ExtraBuildArgs != null) @@ -378,8 +400,14 @@ protected static void AssertDotNetWasmJs(string bundleDir, bool fromRuntimePack) { string nativeDir = GetRuntimeNativeDir(); - AssertFile(Path.Combine(nativeDir, "dotnet.wasm"), Path.Combine(bundleDir, "dotnet.wasm"), "Expected dotnet.wasm to be same as the runtime pack", same: fromRuntimePack); - AssertFile(Path.Combine(nativeDir, "dotnet.js"), Path.Combine(bundleDir, "dotnet.js"), "Expected dotnet.js to be same as the runtime pack", same: fromRuntimePack); + AssertNativeFile("dotnet.wasm"); + AssertNativeFile("dotnet.js"); + + void AssertNativeFile(string file) + => AssertFile(Path.Combine(nativeDir, file), + Path.Combine(bundleDir, file), + $"Expected {file} to be {(fromRuntimePack ? "the same as" : "different from")} the runtime pack", + same: fromRuntimePack); } protected static void AssertFilesDontExist(string dir, string[] filenames, string? label = null) @@ -464,6 +492,7 @@ public static (int exitCode, string buildOutput) RunProcess(string path, _testOutput.WriteLine($"Running {path} {args}"); Console.WriteLine($"Running: {path}: {args}"); Console.WriteLine($"WorkingDirectory: {workingDir}"); + _testOutput.WriteLine($"WorkingDirectory: {workingDir}"); StringBuilder outputBuilder = new (); var processStartInfo = new ProcessStartInfo { @@ -584,7 +613,7 @@ public static int Main() - + @@ -594,5 +623,5 @@ public static int Main() } public record BuildArgs(string ProjectName, string Config, bool AOT, string ProjectFileContents, string? ExtraBuildArgs); - public record BuildProduct(string BuildPath, string LogFile, bool Result); + public record BuildProduct(string ProjectDir, string LogFile, bool Result); } diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/InvariantGlobalizationTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/InvariantGlobalizationTests.cs index 6d01f20a8420e..753f8c40c2af7 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/InvariantGlobalizationTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/InvariantGlobalizationTests.cs @@ -49,7 +49,7 @@ private void TestInvariantGlobalization(BuildArgs buildArgs, bool? invariantGlob extraProperties = $"{extraProperties}{invariantGlobalization}"; buildArgs = buildArgs with { ProjectName = projectName }; - buildArgs = GetBuildArgsWith(buildArgs, extraProperties); + buildArgs = ExpandBuildArgs(buildArgs, extraProperties); if (dotnetWasmFromRuntimePack == null) dotnetWasmFromRuntimePack = !(buildArgs.AOT || buildArgs.Config == "Release"); @@ -76,8 +76,8 @@ public static int Main() hasIcudt: invariantGlobalization == null || invariantGlobalization.Value == false); string expectedOutputString = invariantGlobalization == true - ? "False - en (ES)" - : "True - Invariant Language (Invariant Country)"; + ? "True - Invariant Language (Invariant Country)" + : "False - es (ES)"; RunAndTestWasmApp(buildArgs, expectedExitCode: 42, test: output => Assert.Contains(expectedOutputString, output), host: host, id: id); } diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/MainWithArgsTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/MainWithArgsTests.cs index df920577cea01..b97a0490d2e87 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/MainWithArgsTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/MainWithArgsTests.cs @@ -77,7 +77,7 @@ void TestMainWithArgs(string projectNamePrefix, string programText = projectContents.Replace("##CODE##", code); buildArgs = buildArgs with { ProjectName = projectName, ProjectFileContents = programText }; - buildArgs = GetBuildArgsWith(buildArgs); + buildArgs = ExpandBuildArgs(buildArgs); if (dotnetWasmFromRuntimePack == null) dotnetWasmFromRuntimePack = !(buildArgs.AOT || buildArgs.Config == "Release"); diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs index da7b33d0a0c35..9f87757175c97 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs @@ -39,7 +39,7 @@ public void Relinking_ErrorWhenMissingEMSDK(BuildArgs buildArgs, string emsdkPat ProjectName = projectName, ExtraBuildArgs = $"/p:EMSDK_PATH={emsdkPath}" }; - buildArgs = GetBuildArgsWith(buildArgs, extraProperties: "true"); + buildArgs = ExpandBuildArgs(buildArgs, extraProperties: "true"); (_, string buildOutput) = BuildProject(buildArgs, initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), @@ -54,7 +54,7 @@ private void NativeBuild(string projectNamePrefix, string projectContents, Build string projectName = $"{projectNamePrefix}_{buildArgs.Config}_{buildArgs.AOT}"; buildArgs = buildArgs with { ProjectName = projectName, ProjectFileContents = projectContents }; - buildArgs = GetBuildArgsWith(buildArgs, extraProperties: "true"); + buildArgs = ExpandBuildArgs(buildArgs, extraProperties: "true"); Console.WriteLine ($"-- args: {buildArgs}, name: {projectName}"); BuildProject(buildArgs, diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/RebuildTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/RebuildTests.cs index aebaa43ba5d97..ecbcbdd75c05c 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/RebuildTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/RebuildTests.cs @@ -27,7 +27,7 @@ public void NoOpRebuild(BuildArgs buildArgs, bool nativeRelink, RunHost host, st bool dotnetWasmFromRuntimePack = !nativeRelink && !buildArgs.AOT; buildArgs = buildArgs with { ProjectName = projectName }; - buildArgs = GetBuildArgsWith(buildArgs, $"{(nativeRelink ? "true" : "false")}"); + buildArgs = ExpandBuildArgs(buildArgs, $"{(nativeRelink ? "true" : "false")}"); BuildProject(buildArgs, initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/SatelliteAssembliesTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/SatelliteAssembliesTests.cs new file mode 100644 index 0000000000000..e9d973650dcae --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/SatelliteAssembliesTests.cs @@ -0,0 +1,179 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Xunit; +using Xunit.Abstractions; + +#nullable enable + +namespace Wasm.Build.Tests +{ + public class SatelliteAssembliesTests : BuildTestBase + { + public SatelliteAssembliesTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext) + : base(output, buildContext) + { + } + + public static IEnumerable SatelliteAssemblyTestData(bool aot, bool relinking, RunHost host) + => ConfigWithAOTData(aot) + .Multiply( + new object?[] { relinking, "es-ES", "got: hola" }, + new object?[] { relinking, null, "got: hello" }, + new object?[] { relinking, "ja-JP", "got: \u3053\u3093\u306B\u3061\u306F" }) + .WithRunHosts(host) + .UnwrapItemsAsArrays(); + + [Theory] + [MemberData(nameof(SatelliteAssemblyTestData), parameters: new object[] { /*aot*/ false, /*relinking*/ false, RunHost.All })] + [MemberData(nameof(SatelliteAssemblyTestData), parameters: new object[] { /*aot*/ false, /*relinking*/ true, RunHost.All })] + [MemberData(nameof(SatelliteAssemblyTestData), parameters: new object[] { /*aot*/ true, /*relinking*/ false, RunHost.All })] + public void ResourcesFromMainAssembly(BuildArgs buildArgs, + bool nativeRelink, + string? argCulture, + string expectedOutput, + RunHost host, + string id) + { + string projectName = $"sat_asm_from_main_asm"; + bool dotnetWasmFromRuntimePack = !nativeRelink && !buildArgs.AOT; + + buildArgs = buildArgs with { ProjectName = projectName }; + buildArgs = ExpandBuildArgs(buildArgs, + projectTemplate: s_resourcesProjectTemplate, + extraProperties: $"{(nativeRelink ? "true" : "false")}", + extraItems: $""); + + BuildProject(buildArgs, + initProject: () => CreateProgramForCultureTest($"{projectName}.words", "TestClass"), + dotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack, + id: id); + + string output = RunAndTestWasmApp( + buildArgs, expectedExitCode: 42, + args: argCulture, + host: host, id: id); + + Assert.Contains(expectedOutput, output); + } + + [Theory] + [MemberData(nameof(SatelliteAssemblyTestData), parameters: new object[] { /*aot*/ false, /*relinking*/ false, RunHost.All })] + [MemberData(nameof(SatelliteAssemblyTestData), parameters: new object[] { /*aot*/ false, /*relinking*/ true, RunHost.All })] + [MemberData(nameof(SatelliteAssemblyTestData), parameters: new object[] { /*aot*/ true, /*relinking*/ false, RunHost.All })] + public void ResourcesFromProjectReference(BuildArgs buildArgs, + bool nativeRelink, + string? argCulture, + string expectedOutput, + RunHost host, + string id) + { + string projectName = $"sat_asm_proj_ref"; + bool dotnetWasmFromRuntimePack = !nativeRelink && !buildArgs.AOT; + + buildArgs = buildArgs with { ProjectName = projectName }; + buildArgs = ExpandBuildArgs(buildArgs, + projectTemplate: s_resourcesProjectTemplate, + extraProperties: $"{(nativeRelink ? "true" : "false")}", + extraItems: $""); + + BuildProject(buildArgs, + initProject: () => CreateProgramForCultureTest("LibraryWithResources.words", "LibraryWithResources.Class1"), + dotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack, + id: id); + + string output = RunAndTestWasmApp(buildArgs, + expectedExitCode: 42, + args: argCulture, + host: host, id: id); + + Assert.Contains(expectedOutput, output); + } + +#pragma warning disable xUnit1026 + [Theory] + [BuildAndRun(host: RunHost.None, aot: true)] + public void CheckThatSatelliteAssembliesAreNotAOTed(BuildArgs buildArgs, string id) + { + string projectName = $"check_sat_asm_not_aot"; + buildArgs = buildArgs with { ProjectName = projectName }; + buildArgs = ExpandBuildArgs(buildArgs, + projectTemplate: s_resourcesProjectTemplate, + extraProperties: $@" + -O0 + -O0", + extraItems: $""); + + System.Console.WriteLine ($"--- aot: {buildArgs.AOT}"); + BuildProject(buildArgs, + initProject: () => CreateProgramForCultureTest($"{projectName}.words", "TestClass"), + dotnetWasmFromRuntimePack: false, + id: id); + + var bitCodeFileNames = Directory.GetFileSystemEntries(Path.Combine(_projectDir!, "obj"), "*.dll.bc", SearchOption.AllDirectories) + .Select(path => Path.GetFileName(path)) + .ToArray(); + + // sanity check, in case we change file extensions + Assert.Contains($"{projectName}.dll.bc", bitCodeFileNames); + + Assert.Empty(bitCodeFileNames.Where(file => file.EndsWith(".resources.dll.bc"))); + } +#pragma warning restore xUnit1026 + + private void CreateProgramForCultureTest(string resourceName, string typeName) + => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), + s_cultureResourceTestProgram + .Replace("##RESOURCE_NAME##", resourceName) + .Replace("##TYPE_NAME##", typeName)); + + private const string s_resourcesProjectTemplate = + @$" + + {s_targetFramework} + Exe + true + runtime-test.js + ##EXTRA_PROPERTIES## + + + ##EXTRA_ITEMS## + + ##INSERT_AT_END## + "; + + private static string s_cultureResourceTestProgram = @" +using System; +using System.Runtime.CompilerServices; +using System.Globalization; +using System.Resources; +using System.Threading; + +namespace ResourcesTest +{ + public class TestClass + { + public static int Main(string[] args) + { + if (args.Length == 1) + { + string cultureToTest = args[0]; + var newCulture = new CultureInfo(cultureToTest); + Thread.CurrentThread.CurrentCulture = newCulture; + Thread.CurrentThread.CurrentUICulture = newCulture; + } + + var currentCultureName = Thread.CurrentThread.CurrentCulture.Name; + + var rm = new ResourceManager(""##RESOURCE_NAME##"", typeof(##TYPE_NAME##).Assembly); + Console.WriteLine($""For '{currentCultureName}' got: {rm.GetString(""hello"")}""); + + return 42; + } + } +}"; + } +} diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/SharedBuildPerTestClassFixture.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/SharedBuildPerTestClassFixture.cs index a491d972af28c..e84a151bd5b02 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/SharedBuildPerTestClassFixture.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/SharedBuildPerTestClassFixture.cs @@ -20,7 +20,7 @@ public void CacheBuild(BuildArgs buildArgs, BuildProduct product) public void RemoveFromCache(string buildPath) { - KeyValuePair? foundKvp = _buildPaths.Where(kvp => kvp.Value.BuildPath == buildPath).SingleOrDefault(); + KeyValuePair? foundKvp = _buildPaths.Where(kvp => kvp.Value.ProjectDir == buildPath).SingleOrDefault(); if (foundKvp == null) throw new Exception($"Could not find build path {buildPath} in cache to remove."); @@ -36,7 +36,7 @@ public void Dispose() Console.WriteLine ($"============== DELETING THE BUILDS ============="); foreach (var kvp in _buildPaths.Values) { - RemoveDirectory(kvp.BuildPath); + RemoveDirectory(kvp.ProjectDir); } } diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/Wasm.Build.Tests.csproj b/src/tests/BuildWasmApps/Wasm.Build.Tests/Wasm.Build.Tests.csproj index 9ed56506d799f..20a1972430746 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/Wasm.Build.Tests.csproj +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/Wasm.Build.Tests.csproj @@ -9,6 +9,7 @@ xunit false true + false TEST_DEBUG_CONFIG_ALSO false @@ -20,8 +21,6 @@ <_PreCommand Condition="'$(OS)' != 'Windows_NT'">WasmBuildSupportDir=%24{HELIX_CORRELATION_PAYLOAD}/build <_PreCommand Condition="'$(OS)' == 'Windows_NT'">set WasmBuildSupportDir=%HELIX_CORRELATION_PAYLOAD%/build & - <_PreCommand Condition="'$(OS)' != 'Windows_NT'">$(_PreCommand) DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 - <_PreCommand Condition="'$(OS)' == 'Windows_NT'">$(_PreCommand) set DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 & @@ -38,5 +37,7 @@ + + diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmBuildAppTest.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmBuildAppTest.cs index 3778d1fcdaae3..e426c9523d9f7 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmBuildAppTest.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmBuildAppTest.cs @@ -72,7 +72,7 @@ public void AOT_ErrorWhenMissingEMSDK(BuildArgs buildArgs, string emsdkPath, str ProjectName = projectName, ExtraBuildArgs = $"/p:EMSDK_PATH={emsdkPath}" }; - buildArgs = GetBuildArgsWith(buildArgs); + buildArgs = ExpandBuildArgs(buildArgs); (_, string buildOutput) = BuildProject(buildArgs, initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), @@ -88,6 +88,9 @@ public class TestClass { public static int Main() { Console.WriteLine($""tc: {Environment.TickCount}, tc64: {Environment.TickCount64}""); + + // if this gets printed, then we didn't crash! + Console.WriteLine(""Hello, World!""); return 42; } }"; @@ -109,11 +112,11 @@ void TestMain(string projectName, BuildArgs buildArgs, RunHost host, string id, - string? extraProperties = null, + string extraProperties = "", bool? dotnetWasmFromRuntimePack = null) { buildArgs = buildArgs with { ProjectName = projectName }; - buildArgs = GetBuildArgsWith(buildArgs, extraProperties); + buildArgs = ExpandBuildArgs(buildArgs, extraProperties); if (dotnetWasmFromRuntimePack == null) dotnetWasmFromRuntimePack = !(buildArgs.AOT || buildArgs.Config == "Release"); diff --git a/src/tests/BuildWasmApps/testassets/LibraryWithResources/Class1.cs b/src/tests/BuildWasmApps/testassets/LibraryWithResources/Class1.cs new file mode 100644 index 0000000000000..4674bfd7d0b34 --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/LibraryWithResources/Class1.cs @@ -0,0 +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; + +namespace LibraryWithResources +{ + public class Class1 + { + } +} diff --git a/src/tests/BuildWasmApps/testassets/LibraryWithResources/Directory.Build.props b/src/tests/BuildWasmApps/testassets/LibraryWithResources/Directory.Build.props new file mode 100644 index 0000000000000..058246e408620 --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/LibraryWithResources/Directory.Build.props @@ -0,0 +1 @@ + diff --git a/src/tests/BuildWasmApps/testassets/LibraryWithResources/Directory.Build.targets b/src/tests/BuildWasmApps/testassets/LibraryWithResources/Directory.Build.targets new file mode 100644 index 0000000000000..058246e408620 --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/LibraryWithResources/Directory.Build.targets @@ -0,0 +1 @@ + diff --git a/src/tests/BuildWasmApps/testassets/LibraryWithResources/LibraryWithResources.csproj b/src/tests/BuildWasmApps/testassets/LibraryWithResources/LibraryWithResources.csproj new file mode 100644 index 0000000000000..91961dd7d6b42 --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/LibraryWithResources/LibraryWithResources.csproj @@ -0,0 +1,10 @@ + + + + net5.0 + + + + + + diff --git a/src/tests/BuildWasmApps/testassets/resx/words.es-ES.resx b/src/tests/BuildWasmApps/testassets/resx/words.es-ES.resx new file mode 100644 index 0000000000000..775397b15a2b9 --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/resx/words.es-ES.resx @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ciao + + + + hola + + diff --git a/src/tests/BuildWasmApps/testassets/resx/words.ja-JP.resx b/src/tests/BuildWasmApps/testassets/resx/words.ja-JP.resx new file mode 100644 index 0000000000000..c843811244c25 --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/resx/words.ja-JP.resx @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + さようなら + + + + こんにちは + + diff --git a/src/tests/BuildWasmApps/testassets/resx/words.resx b/src/tests/BuildWasmApps/testassets/resx/words.resx new file mode 100644 index 0000000000000..c3d5a78742086 --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/resx/words.resx @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + bye + + + + hello + + From 98db254b38c71713ca7a2f7e1240f01de73c022c Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Tue, 22 Jun 2021 12:44:55 -0400 Subject: [PATCH 060/107] Import the correct MacCatalyst workload pack (#54558) In https://github.com/dotnet/runtime/pull/54361 there was an incorrect import of the maccatalyst aot workload pack. This fix corrects the problem. Fixes https://github.com/dotnet/runtime/issues/54494 --- .../WorkloadManifest.targets | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets index 0e454c9bd5013..ca7309a6612b4 100644 --- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets +++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets @@ -26,8 +26,7 @@ - - + From 62b8d02fdbf4288bb1cb3ea60d31a96b0f9dd14f Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Tue, 22 Jun 2021 09:57:09 -0700 Subject: [PATCH 061/107] Disable failing tests under GCStress (#54532) https://github.com/dotnet/runtime/issues/51477 https://github.com/dotnet/runtime/issues/53359 --- .../ObjectiveC/ObjectiveCMarshalAPI/ObjectiveCMarshalAPI.csproj | 2 ++ .../eventpipe/processenvironment/processenvironment.csproj | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/tests/Interop/ObjectiveC/ObjectiveCMarshalAPI/ObjectiveCMarshalAPI.csproj b/src/tests/Interop/ObjectiveC/ObjectiveCMarshalAPI/ObjectiveCMarshalAPI.csproj index 9a81c001eb8cd..7ae26166948d1 100644 --- a/src/tests/Interop/ObjectiveC/ObjectiveCMarshalAPI/ObjectiveCMarshalAPI.csproj +++ b/src/tests/Interop/ObjectiveC/ObjectiveCMarshalAPI/ObjectiveCMarshalAPI.csproj @@ -4,6 +4,8 @@ true true + + true diff --git a/src/tests/tracing/eventpipe/processenvironment/processenvironment.csproj b/src/tests/tracing/eventpipe/processenvironment/processenvironment.csproj index 8633e6ca18836..97976e33e50c4 100644 --- a/src/tests/tracing/eventpipe/processenvironment/processenvironment.csproj +++ b/src/tests/tracing/eventpipe/processenvironment/processenvironment.csproj @@ -8,6 +8,8 @@ true true true + + true From 245dddc95ce85ee810cdcbd0727662d4c716dbe6 Mon Sep 17 00:00:00 2001 From: Fan Yang <52458914+fanyang-mono@users.noreply.github.com> Date: Tue, 22 Jun 2021 13:36:57 -0400 Subject: [PATCH 062/107] [mono]Re-enable runtime tests on Android arm64 (#49662) * Re-enable runtime tests on Android arm64 * disable irrelevant CI lanes * Disable more dotnet-linker-tests and runtime-dev-innerloop CI lanes * Comment out definition for DISABLE_LOGGING * Remove defining logging for android * Extend app installation timeout limit * Fix command prefix for windows * Enable tests excluded only for Android arm64 * Disable failed test on arm64 * Revert hacks * Revert unintended changes * More... * Only run tests on arm64 for rolling build * Fix merge error --- eng/pipelines/runtime-staging.yml | 14 +++++++----- .../Coreclr.TestWrapper/MobileAppHandler.cs | 16 +++++++++++--- src/tests/issues.targets | 22 ++----------------- 3 files changed, 23 insertions(+), 29 deletions(-) diff --git a/eng/pipelines/runtime-staging.yml b/eng/pipelines/runtime-staging.yml index 5def476efefd6..36ff5c79c00ed 100644 --- a/eng/pipelines/runtime-staging.yml +++ b/eng/pipelines/runtime-staging.yml @@ -347,7 +347,7 @@ jobs: buildConfig: Release runtimeFlavor: mono platforms: - #- Android_arm64 # disabled due to https://github.com/dotnet/runtime/issues/47850 + - Android_arm64 variables: - ${{ if and(eq(variables['System.TeamProject'], 'public'), eq(variables['Build.Reason'], 'PullRequest')) }}: - name: _HelixSource @@ -369,11 +369,13 @@ jobs: eq(dependencies.evaluate_paths.outputs['SetPathVars_runtimetests.containsChange'], true), eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true), eq(variables['isFullMatrix'], true)) - # extra steps, run tests - extraStepsTemplate: /eng/pipelines/common/templates/runtimes/android-runtime-and-send-to-helix.yml - extraStepsParameters: - creator: dotnet-bot - testRunNamePrefixSuffix: Mono_$(_BuildConfig) + # don't run tests on PRs until we can get significantly more devices + ${{ if eq(variables['isFullMatrix'], true) }}: + # extra steps, run tests + extraStepsTemplate: /eng/pipelines/common/templates/runtimes/android-runtime-and-send-to-helix.yml + extraStepsParameters: + creator: dotnet-bot + testRunNamePrefixSuffix: Mono_$(_BuildConfig) # Run disabled installer tests on Linux x64 - template: /eng/pipelines/common/platform-matrix.yml diff --git a/src/tests/Common/Coreclr.TestWrapper/MobileAppHandler.cs b/src/tests/Common/Coreclr.TestWrapper/MobileAppHandler.cs index 7a80d1e2b3b7a..b712246c6f340 100644 --- a/src/tests/Common/Coreclr.TestWrapper/MobileAppHandler.cs +++ b/src/tests/Common/Coreclr.TestWrapper/MobileAppHandler.cs @@ -29,7 +29,7 @@ private static void HandleMobileApp(string action, string platform, string categ string xharnessCmd; string cmdStr; string appExtension; - int timeout = 240000; // Set timeout to 4 mins, because the installation on Android arm64/32 devices could take up to 4 mins on CI + int timeout = 600000; // Set timeout to 4 mins, because the installation on Android arm64/32 devices could take up to 10 mins on CI if(String.IsNullOrEmpty(dotnetCmd_raw)) { @@ -120,8 +120,18 @@ private static void HandleMobileApp(string action, string platform, string categ private static string ConvertCmd2Arg(string cmd) { cmd.Replace("\"", "\"\""); - var result = $"-c \"{cmd}\""; - return result; + + string cmdPrefix; + if(OperatingSystem.IsWindows()) + { + cmdPrefix = "/c"; + } + else + { + cmdPrefix = "-c"; + } + + return $"{cmdPrefix} \"{cmd}\""; } } } diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 1daa9fda46d9e..e34d49e145543 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -3146,26 +3146,8 @@ - - https://github.com/dotnet/runtime/issues/45568 (workitem JIT.HardwareIntrinsics) - - - https://github.com/dotnet/runtime/issues/45568 (workitem JIT.Methodical*) - - - https://github.com/dotnet/runtime/issues/45568 (workitem JIT.IL_Conformance) - - - https://github.com/dotnet/runtime/issues/45568 (workitem JIT) - - - https://github.com/dotnet/runtime/issues/45568 (workitem JIT) - - - https://github.com/dotnet/runtime/issues/45568 (workitem JIT.Regression.CLR-x86-JIT.V1-M12-M13) - - - https://github.com/dotnet/runtime/issues/45568 (workitem PayloadGroup0) + + https://github.com/dotnet/runtime/issues/52781 From 4fd380a71666afb8572b5e6559cafcbd72a469b9 Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Tue, 22 Jun 2021 15:00:17 -0400 Subject: [PATCH 063/107] [Android] Fix AndroidAppBuilder to work w/ AOT+LLVM (#53643) We were missing a few key additions to make sure we can test against AOT+LLVM. This change will make sure we link against all the .dll-llvm.o files produced from the AOT compiler and include the right mtriple for each architecture. Fixes #53628 --- src/mono/mono/mini/aot-compiler.c | 16 ++++++----- src/mono/mono/mini/mini-arm.c | 2 ++ src/tasks/AndroidAppBuilder/ApkBuilder.cs | 27 ++++++++++++------- .../Templates/CMakeLists-android.txt | 9 ++++++- .../AotCompilerTask/MonoAOTCompiler.props | 5 +++- ...droid.Device_Emulator.Aot_Llvm.Test.csproj | 17 ++++++++++++ .../Device_Emulator/AOT_LLVM/Program.cs | 13 +++++++++ 7 files changed, 72 insertions(+), 17 deletions(-) create mode 100644 src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/Android.Device_Emulator.Aot_Llvm.Test.csproj create mode 100644 src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/Program.cs diff --git a/src/mono/mono/mini/aot-compiler.c b/src/mono/mono/mini/aot-compiler.c index 358c4e60c53fd..b36fea0cdf422 100644 --- a/src/mono/mono/mini/aot-compiler.c +++ b/src/mono/mono/mini/aot-compiler.c @@ -1115,17 +1115,15 @@ arch_init (MonoAotCompile *acfg) acfg->llvm_label_prefix = ""; acfg->user_symbol_prefix = ""; -#if defined(TARGET_X86) -#ifdef TARGET_ANDROID - g_string_append_printf (acfg->llc_args, " -mtriple=i686-none-linux-android21"); -#else +#if TARGET_X86 || TARGET_AMD64 const gboolean has_custom_args = !!acfg->aot_opts.llvm_llc || acfg->aot_opts.use_current_cpu; - g_string_append_printf (acfg->llc_args, " -march=x86 %s", has_custom_args ? "" : "-mcpu=generic"); #endif + +#if defined(TARGET_X86) + g_string_append_printf (acfg->llc_args, " -march=x86 %s", has_custom_args ? "" : "-mcpu=generic"); #endif #if defined(TARGET_AMD64) - const gboolean has_custom_args = !!acfg->aot_opts.llvm_llc || acfg->aot_opts.use_current_cpu; g_string_append_printf (acfg->llc_args, " -march=x86-64 %s", has_custom_args ? "" : "-mcpu=generic"); /* NOP */ acfg->align_pad_value = 0x90; @@ -1159,7 +1157,13 @@ arch_init (MonoAotCompile *acfg) g_string_append (acfg->llc_args, " -mattr=+vfp2,-neon,+d16 -float-abi=hard"); g_string_append (acfg->as_args, " -mfpu=vfp3"); #elif defined(ARM_FPU_VFP) + +#if defined(TARGET_ARM) + // +d16 triggers a warning on arm + g_string_append (acfg->llc_args, " -mattr=+vfp2,-neon"); +#else g_string_append (acfg->llc_args, " -mattr=+vfp2,-neon,+d16"); +#endif g_string_append (acfg->as_args, " -mfpu=vfp3"); #else g_string_append (acfg->llc_args, " -mattr=+soft-float"); diff --git a/src/mono/mono/mini/mini-arm.c b/src/mono/mono/mini/mini-arm.c index 1f4ec81d761f4..4fc43ee307166 100644 --- a/src/mono/mono/mini/mini-arm.c +++ b/src/mono/mono/mini/mini-arm.c @@ -873,6 +873,8 @@ mono_arch_init (void) have a way to properly detect CPU features on it. */ thumb_supported = TRUE; iphone_abi = TRUE; +#elif defined(TARGET_ANDROID) + thumb_supported = TRUE; #else thumb_supported = mono_hwcap_arm_has_thumb; thumb2_supported = mono_hwcap_arm_has_thumb2; diff --git a/src/tasks/AndroidAppBuilder/ApkBuilder.cs b/src/tasks/AndroidAppBuilder/ApkBuilder.cs index ca7c4922ddbbc..346f854c383e6 100644 --- a/src/tasks/AndroidAppBuilder/ApkBuilder.cs +++ b/src/tasks/AndroidAppBuilder/ApkBuilder.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text; using Microsoft.Build.Framework; public class ApkBuilder @@ -122,18 +123,29 @@ public class ApkBuilder throw new ArgumentException($"{buildToolsFolder} was not found."); } - var assemblerFiles = new List(); + var assemblerFiles = new StringBuilder(); + var assemblerFilesToLink = new StringBuilder(); foreach (ITaskItem file in Assemblies) { // use AOT files if available var obj = file.GetMetadata("AssemblerFile"); + var llvmObj = file.GetMetadata("LlvmObjectFile"); + if (!string.IsNullOrEmpty(obj)) { - assemblerFiles.Add(obj); + var name = Path.GetFileNameWithoutExtension(obj); + assemblerFiles.AppendLine($"add_library({name} OBJECT {obj})"); + assemblerFilesToLink.AppendLine($" {name}"); + } + + if (!string.IsNullOrEmpty(llvmObj)) + { + var name = Path.GetFileNameWithoutExtension(llvmObj); + assemblerFilesToLink.AppendLine($" {llvmObj}"); } } - if (ForceAOT && !assemblerFiles.Any()) + if (ForceAOT && assemblerFiles.Length == 0) { throw new InvalidOperationException("Need list of AOT files."); } @@ -261,12 +273,9 @@ public class ApkBuilder nativeLibraries += $" {monoRuntimeLib}{Environment.NewLine}"; } - string aotSources = ""; - foreach (string asm in assemblerFiles) - { - // these libraries are linked via modules.c - aotSources += $" {asm}{Environment.NewLine}"; - } + nativeLibraries += assemblerFilesToLink.ToString(); + + string aotSources = assemblerFiles.ToString(); string cmakeLists = Utils.GetEmbeddedResource("CMakeLists-android.txt") .Replace("%MonoInclude%", monoRuntimeHeaders) diff --git a/src/tasks/AndroidAppBuilder/Templates/CMakeLists-android.txt b/src/tasks/AndroidAppBuilder/Templates/CMakeLists-android.txt index c74d4625af0fa..7b602ad6a3029 100644 --- a/src/tasks/AndroidAppBuilder/Templates/CMakeLists-android.txt +++ b/src/tasks/AndroidAppBuilder/Templates/CMakeLists-android.txt @@ -9,12 +9,19 @@ if(NOT USE_LLVM) add_compile_options(-no-integrated-as) endif() +# Prevent the warning: shared library text segment is not shareable which is treated as an error +if (NOT ANDROID_ABI STREQUAL "arm64-v8a") + add_link_options(-Wl,--no-warn-shared-textrel) +endif() + add_library( monodroid SHARED monodroid.c %AotModulesSource% - %AotSources%) +) + +%AotSources% %Defines% diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.props b/src/tasks/AotCompilerTask/MonoAOTCompiler.props index 097758ae0c783..159c01ff3f27b 100644 --- a/src/tasks/AotCompilerTask/MonoAOTCompiler.props +++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.props @@ -14,7 +14,10 @@ - + + + + diff --git a/src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/Android.Device_Emulator.Aot_Llvm.Test.csproj b/src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/Android.Device_Emulator.Aot_Llvm.Test.csproj new file mode 100644 index 0000000000000..3ec5bf5e4b63d --- /dev/null +++ b/src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/Android.Device_Emulator.Aot_Llvm.Test.csproj @@ -0,0 +1,17 @@ + + + Exe + false + true + true + $(NetCoreAppCurrent) + Android.Device_Emulator.Aot_Llvm.Test.dll + 42 + true + true + + + + + + diff --git a/src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/Program.cs b/src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/Program.cs new file mode 100644 index 0000000000000..7dcc0f375db87 --- /dev/null +++ b/src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/Program.cs @@ -0,0 +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; + +public static class Program +{ + public static int Main(string[] args) + { + Console.WriteLine("Hello, Android!"); // logcat + return 42; + } +} From de0a408d34045a5e831225b81d5860a8b59ddfdd Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Tue, 22 Jun 2021 13:12:05 -0700 Subject: [PATCH 064/107] Disable failing test BasicTestWithMcj under GCStress (#54566) Tracking: https://github.com/dotnet/runtime/issues/54203 --- .../baseservices/TieredCompilation/BasicTestWithMcj.csproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tests/baseservices/TieredCompilation/BasicTestWithMcj.csproj b/src/tests/baseservices/TieredCompilation/BasicTestWithMcj.csproj index d4ed48b297d47..a25cf03108964 100644 --- a/src/tests/baseservices/TieredCompilation/BasicTestWithMcj.csproj +++ b/src/tests/baseservices/TieredCompilation/BasicTestWithMcj.csproj @@ -4,6 +4,8 @@ true true 0 + + true true From 26857bff9b62e87083ef1ba4ec0c45b3b58f3095 Mon Sep 17 00:00:00 2001 From: Maoni Stephens Date: Tue, 22 Jun 2021 13:27:31 -0700 Subject: [PATCH 065/107] get rid of an unnecessary join (#54312) I think this join was left there by accident when we were doing the write watch feature. I'm puzzled why I didn't notice it. the comment was also inconsistent which strengthens my belief that this was a mistake. we don't need a join here (there used to not be a join) and disabling the dirty pages tracking can be done anywhere while the EE is stopped since write barriers cannot be invoked. --- src/coreclr/gc/gc.cpp | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index f4fdf3177ef86..6f99f1ad78d8d 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -641,6 +641,10 @@ process_sync_log_stats() #ifndef DACCESS_COMPILE uint32_t g_num_active_processors = 0; +// Note that when a join is no longer used we still keep the values here because +// tooling already recognized them as having the meaning they were assigned originally. +// It doesn't break tooling if we stop using them but does if we assign a new meaning +// to them. enum gc_join_stage { gc_join_init_cpu_mapping = 0, @@ -681,6 +685,7 @@ enum gc_join_stage gc_join_after_commit_soh_no_gc = 35, gc_join_expand_loh_no_gc = 36, gc_join_final_no_gc = 37, + // No longer in use but do not remove, see comments for this enum. gc_join_disable_software_write_watch = 38, gc_join_max = 39 }; @@ -31805,23 +31810,6 @@ void gc_heap::background_mark_phase () //concurrent_print_time_delta ("nonconcurrent revisit dirtied pages on LOH"); concurrent_print_time_delta ("NRre LOH"); -#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP -#ifdef MULTIPLE_HEAPS - bgc_t_join.join(this, gc_join_disable_software_write_watch); - if (bgc_t_join.joined()) -#endif // MULTIPLE_HEAPS - { - // The runtime is suspended, and we will be doing a final query of dirty pages, so pause tracking written pages to - // avoid further perf penalty after the runtime is restarted - SoftwareWriteWatch::DisableForGCHeap(); - -#ifdef MULTIPLE_HEAPS - dprintf(3, ("Restarting BGC threads after disabling software write watch")); - bgc_t_join.restart(); -#endif // MULTIPLE_HEAPS - } -#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP - dprintf (2, ("before NR 1st Hov count: %d", bgc_overflow_count)); bgc_overflow_count = 0; @@ -31846,6 +31834,12 @@ void gc_heap::background_mark_phase () if (bgc_t_join.joined()) #endif //MULTIPLE_HEAPS { +#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + // The runtime is suspended, take this opportunity to pause tracking written pages to + // avoid further perf penalty after the runtime is restarted + SoftwareWriteWatch::DisableForGCHeap(); +#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + GCToEEInterface::AfterGcScanRoots (max_generation, max_generation, &sc); #ifdef MULTIPLE_HEAPS From d0a102843040a3950827c498fa05da6e9f55a2b9 Mon Sep 17 00:00:00 2001 From: Andrew Au Date: Tue, 22 Jun 2021 13:28:59 -0700 Subject: [PATCH 066/107] Fix a memory corruption caused by an integer overflow (#54510) --- src/coreclr/gc/gc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 6f99f1ad78d8d..6a6c63d1ac29d 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -10813,7 +10813,7 @@ void gc_heap::return_free_region (heap_segment* region) heap_segment_mem (region), num_basic_regions, num_free_regions)); for (int i = 0; i < num_basic_regions; i++) { - uint8_t* basic_region_start = region_start + (i << min_segment_size_shr); + uint8_t* basic_region_start = region_start + ((size_t)i << min_segment_size_shr); heap_segment* basic_region = get_region_info (basic_region_start); heap_segment_allocated (basic_region) = 0; #ifdef MULTIPLE_HEAPS From 9da9bfcaffca3329ad9efdfccd89ef9e9aaf2930 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Tue, 22 Jun 2021 14:07:54 -0700 Subject: [PATCH 067/107] Improve performance of IsRootScope check (#54555) - Stash a field instead of doing an equality comparison. Fixes #54351 --- .../src/ServiceLookup/ServiceProviderEngineScope.cs | 5 +++-- .../src/ServiceProvider.cs | 4 ++-- .../tests/DI.Tests/ServiceProviderEngineScopeTests.cs | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngineScope.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngineScope.cs index 06498ff98baac..4ad8b1579a029 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngineScope.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngineScope.cs @@ -16,10 +16,11 @@ internal sealed class ServiceProviderEngineScope : IServiceScope, IServiceProvid private bool _disposed; private List _disposables; - public ServiceProviderEngineScope(ServiceProvider provider) + public ServiceProviderEngineScope(ServiceProvider provider, bool isRootScope) { ResolvedServices = new Dictionary(); RootProvider = provider; + IsRootScope = isRootScope; } internal Dictionary ResolvedServices { get; } @@ -29,7 +30,7 @@ public ServiceProviderEngineScope(ServiceProvider provider) // For other scopes, it protects ResolvedServices and the list of disposables internal object Sync => ResolvedServices; - public bool IsRootScope => this == RootProvider.Root; + public bool IsRootScope { get; } internal ServiceProvider RootProvider { get; } diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs index d21d9807f5f77..fb7052ec8ea66 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs @@ -36,7 +36,7 @@ internal ServiceProvider(IEnumerable serviceDescriptors, Serv _createServiceAccessor = CreateServiceAccessor; _realizedServices = new ConcurrentDictionary>(); - Root = new ServiceProviderEngineScope(this); + Root = new ServiceProviderEngineScope(this, isRootScope: true); CallSiteFactory = new CallSiteFactory(serviceDescriptors); // The list of built in services that aren't part of the list of service descriptors // keep this in sync with CallSiteFactory.IsService @@ -173,7 +173,7 @@ internal IServiceScope CreateScope() ThrowHelper.ThrowObjectDisposedException(); } - return new ServiceProviderEngineScope(this); + return new ServiceProviderEngineScope(this, isRootScope: false); } private ServiceProviderEngine GetEngine() 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 5c19caa922e76..d43752db21eba 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderEngineScopeTests.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderEngineScopeTests.cs @@ -12,7 +12,7 @@ public class ServiceProviderEngineScopeTests public void DoubleDisposeWorks() { var provider = new ServiceProvider(new ServiceCollection(), ServiceProviderOptions.Default); - var serviceProviderEngineScope = new ServiceProviderEngineScope(provider); + var serviceProviderEngineScope = new ServiceProviderEngineScope(provider, isRootScope: true); serviceProviderEngineScope.ResolvedServices.Add(new ServiceCacheKey(typeof(IFakeService), 0), null); serviceProviderEngineScope.Dispose(); serviceProviderEngineScope.Dispose(); From 7a3343f22f7caab6cd04c94e8ce8e34971e791d3 Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Tue, 22 Jun 2021 17:25:36 -0400 Subject: [PATCH 068/107] Add `SkipEnabledCheck` on `LoggerMessageAttribute` (#54305) * Add SkipEnabledCheck on LoggerMessageAttribute * Make logging generator more robust with null input * Adds LoggerMessageAttribute ctor overload * properly identify misconfigured input --- .../gen/LoggerMessageGenerator.Emitter.cs | 35 ++++-- .../gen/LoggerMessageGenerator.Parser.cs | 114 +++++++++++++----- ...crosoft.Extensions.Logging.Abstractions.cs | 2 + .../src/LoggerMessageAttribute.cs | 19 +++ .../TestWithDefaultValues.generated.txt | 57 +++++++++ .../TestWithSkipEnabledCheck.generated.txt | 18 +++ .../LoggerMessageGeneratedCodeTests.cs | 58 +++++++++ .../LoggerMessageGeneratorEmitterTests.cs | 42 ++++++- .../LoggerMessageGeneratorParserTests.cs | 104 ++++++++++++++++ .../TestClasses/EventNameTestExtensions.cs | 3 + .../TestClasses/LevelTestExtensions.cs | 3 + .../TestClasses/MessageTestExtensions.cs | 6 + .../TestClasses/SkipEnabledCheckExtensions.cs | 15 +++ 13 files changed, 428 insertions(+), 48 deletions(-) create mode 100644 src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithDefaultValues.generated.txt create mode 100644 src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithSkipEnabledCheck.generated.txt create mode 100644 src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/SkipEnabledCheckExtensions.cs diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Emitter.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Emitter.cs index 2b0bc4eca00d5..908efaf6afe32 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Emitter.cs @@ -381,34 +381,45 @@ private void GenLogMethod(LoggerMethod lm, string nestedIndentation) GenParameters(lm); _builder.Append($@") - {nestedIndentation}{{ + {nestedIndentation}{{"); + + string enabledCheckIndentation = lm.SkipEnabledCheck ? "" : " "; + if (!lm.SkipEnabledCheck) + { + _builder.Append($@" {nestedIndentation}if ({logger}.IsEnabled({level})) {nestedIndentation}{{"); + } if (UseLoggerMessageDefine(lm)) { _builder.Append($@" - {nestedIndentation}__{lm.Name}Callback({logger}, "); + {nestedIndentation}{enabledCheckIndentation}__{lm.Name}Callback({logger}, "); GenCallbackArguments(lm); - _builder.Append(@$"{exceptionArg});"); + _builder.Append($"{exceptionArg});"); } else { _builder.Append($@" - {nestedIndentation}{logger}.Log( - {level}, - new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), - "); - GenHolder(lm); - _builder.Append($@", - {exceptionArg}, - __{lm.Name}Struct.Format);"); + {nestedIndentation}{enabledCheckIndentation}{logger}.Log( + {nestedIndentation}{enabledCheckIndentation}{level}, + {nestedIndentation}{enabledCheckIndentation}new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), + {nestedIndentation}{enabledCheckIndentation}"); + GenHolder(lm); + _builder.Append($@", + {nestedIndentation}{enabledCheckIndentation}{exceptionArg}, + {nestedIndentation}{enabledCheckIndentation}__{lm.Name}Struct.Format);"); + } + + if (!lm.SkipEnabledCheck) + { + _builder.Append($@" + {nestedIndentation}}}"); } _builder.Append($@" - {nestedIndentation}}} {nestedIndentation}}}"); static string GetException(LoggerMethod lm) diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Parser.cs index 534249c232601..4a995039104b9 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Parser.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using System.Collections.Immutable; namespace Microsoft.Extensions.Logging.Generators { @@ -28,7 +29,7 @@ public Parser(Compilation compilation, Action reportDiagnostic, Canc } /// - /// Gets the set of logging classes or structs containing methods to output. + /// Gets the set of logging classes containing methods to output. /// public IReadOnlyList GetLogClasses(IEnumerable classes) { @@ -83,7 +84,7 @@ public IReadOnlyList GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable? boundAttrbutes = logMethodSymbol?.GetAttributes(); + + if (boundAttrbutes == null) + { + continue; + } + + foreach (AttributeData attributeData in boundAttrbutes) + { + // supports: [LoggerMessage(0, LogLevel.Warning, "custom message")] + // supports: [LoggerMessage(eventId: 0, level: LogLevel.Warning, message: "custom message")] + if (attributeData.ConstructorArguments.Any()) + { + foreach (TypedConstant typedConstant in attributeData.ConstructorArguments) + { + if (typedConstant.Kind == TypedConstantKind.Error) + { + hasMisconfiguredInput = true; + } + } + + ImmutableArray items = attributeData.ConstructorArguments; + Debug.Assert(items.Length == 3); + + eventId = items[0].IsNull ? -1 : (int)GetItem(items[0]); + level = items[1].IsNull ? null : (int?)GetItem(items[1]); + message = items[2].IsNull ? "" : (string)GetItem(items[2]); + } + + // argument syntax takes parameters. e.g. EventId = 0 + // supports: e.g. [LoggerMessage(EventId = 0, Level = LogLevel.Warning, Message = "custom message")] + if (attributeData.NamedArguments.Any()) + { + foreach (KeyValuePair namedArgument in attributeData.NamedArguments) + { + TypedConstant typedConstant = namedArgument.Value; + if (typedConstant.Kind == TypedConstantKind.Error) + { + hasMisconfiguredInput = true; + } + else + { + TypedConstant value = namedArgument.Value; + switch (namedArgument.Key) + { + case "EventId": + eventId = (int)GetItem(value); + break; + case "Level": + level = value.IsNull ? null : (int?)GetItem(value); + break; + case "SkipEnabledCheck": + skipEnabledCheck = (bool)GetItem(value); + break; + case "EventName": + eventName = (string?)GetItem(value); + break; + case "Message": + message = value.IsNull ? "" : (string)GetItem(value); + break; + } + } + } + } + } + + if (hasMisconfiguredInput) + { + // skip further generator execution and let compiler generate the errors + break; + } IMethodSymbol? methodSymbol = sm.GetDeclaredSymbol(method, _cancellationToken); if (methodSymbol != null) @@ -119,6 +194,7 @@ public IReadOnlyList GetLogClasses(IEnumerable return (loggerField, false); } - private (int eventId, int? level, string message, string? eventName) ExtractAttributeValues(AttributeArgumentListSyntax args, SemanticModel sm) - { - int eventId = 0; - int? level = null; - string? eventName = null; - string message = string.Empty; - foreach (AttributeArgumentSyntax a in args.Arguments) - { - // argument syntax takes parameters. e.g. EventId = 0 - Debug.Assert(a.NameEquals != null); - switch (a.NameEquals.Name.ToString()) - { - case "EventId": - eventId = (int)sm.GetConstantValue(a.Expression, _cancellationToken).Value!; - break; - case "EventName": - eventName = sm.GetConstantValue(a.Expression, _cancellationToken).ToString(); - break; - case "Level": - level = (int)sm.GetConstantValue(a.Expression, _cancellationToken).Value!; - break; - case "Message": - message = sm.GetConstantValue(a.Expression, _cancellationToken).ToString(); - break; - } - } - return (eventId, level, message, eventName); - } - private void Diag(DiagnosticDescriptor desc, Location? location, params object?[]? messageArgs) { _reportDiagnostic(Diagnostic.Create(desc, location, messageArgs)); @@ -580,6 +627,8 @@ private string GetStringExpression(SemanticModel sm, SyntaxNode expr) return string.Empty; } + + private static object GetItem(TypedConstant arg) => arg.Kind == TypedConstantKind.Array ? arg.Values : arg.Value; } /// @@ -612,6 +661,7 @@ internal class LoggerMethod public bool IsExtensionMethod; public string Modifiers = string.Empty; public string LoggerField = string.Empty; + public bool SkipEnabledCheck; } /// diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/ref/Microsoft.Extensions.Logging.Abstractions.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/ref/Microsoft.Extensions.Logging.Abstractions.cs index e7b9034ea81af..ef9d46564ec61 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/ref/Microsoft.Extensions.Logging.Abstractions.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/ref/Microsoft.Extensions.Logging.Abstractions.cs @@ -119,10 +119,12 @@ public static partial class LoggerMessage public sealed partial class LoggerMessageAttribute : System.Attribute { public LoggerMessageAttribute() { } + public LoggerMessageAttribute(int eventId, Microsoft.Extensions.Logging.LogLevel level, string message) { } public int EventId { get { throw null; } set { } } public string? EventName { get { throw null; } set { } } public Microsoft.Extensions.Logging.LogLevel Level { get { throw null; } set { } } public string Message { get { throw null; } set { } } + public bool SkipEnabledCheck { get { throw null; } set { } } } public partial class Logger : Microsoft.Extensions.Logging.ILogger, Microsoft.Extensions.Logging.ILogger { diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LoggerMessageAttribute.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LoggerMessageAttribute.cs index b103ef31a4e16..acb9af3d8601f 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LoggerMessageAttribute.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LoggerMessageAttribute.cs @@ -37,6 +37,20 @@ public sealed class LoggerMessageAttribute : Attribute /// public LoggerMessageAttribute() { } + /// + /// Initializes a new instance of the class + /// which is used to guide the production of a strongly-typed logging method. + /// + /// The log event Id. + /// The log level. + /// Format string of the log message. + public LoggerMessageAttribute(int eventId, LogLevel level, string message) + { + EventId = eventId; + Level = level; + Message = message; + } + /// /// Gets the logging event id for the logging method. /// @@ -59,5 +73,10 @@ public LoggerMessageAttribute() { } /// Gets the message text for the logging method. /// public string Message { get; set; } = ""; + + /// + /// Gets the flag to skip IsEnabled check for the logging method. + /// + public bool SkipEnabledCheck { get; set; } } } diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithDefaultValues.generated.txt b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithDefaultValues.generated.txt new file mode 100644 index 0000000000000..c61da37d27bba --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithDefaultValues.generated.txt @@ -0,0 +1,57 @@ +// +#nullable enable + +namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses +{ + partial class TestWithDefaultValues + { + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] + private readonly struct __M0Struct : global::System.Collections.Generic.IReadOnlyList> + { + + public override string ToString() + { + + return $""; + } + + public static string Format(__M0Struct state, global::System.Exception? ex) => state.ToString(); + + public int Count => 1; + + public global::System.Collections.Generic.KeyValuePair this[int index] + { + get => index switch + { + 0 => new global::System.Collections.Generic.KeyValuePair("{OriginalFormat}", ""), + + _ => throw new global::System.IndexOutOfRangeException(nameof(index)), // return the same exception LoggerMessage.Define returns in this case + }; + } + + public global::System.Collections.Generic.IEnumerator> GetEnumerator() + { + for (int i = 0; i < 1; i++) + { + yield return this[i]; + } + } + + global::System.Collections.IEnumerator global::System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] + public static partial void M0(global::Microsoft.Extensions.Logging.ILogger logger, global::Microsoft.Extensions.Logging.LogLevel level) + { + if (logger.IsEnabled(level)) + { + logger.Log( + level, + new global::Microsoft.Extensions.Logging.EventId(-1, nameof(M0)), + new __M0Struct(), + null, + __M0Struct.Format); + } + } + } +} diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithSkipEnabledCheck.generated.txt b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithSkipEnabledCheck.generated.txt new file mode 100644 index 0000000000000..c4b242a0a9e17 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithSkipEnabledCheck.generated.txt @@ -0,0 +1,18 @@ +// +#nullable enable + +namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses +{ + partial class TestWithSkipEnabledCheck + { + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] + private static readonly global::System.Action __M0Callback = + global::Microsoft.Extensions.Logging.LoggerMessage.Define(global::Microsoft.Extensions.Logging.LogLevel.Information, new global::Microsoft.Extensions.Logging.EventId(0, nameof(M0)), "Message: When using SkipEnabledCheck, the generated code skips logger.IsEnabled(logLevel) check before calling log. To be used when consumer has already guarded logger method in an IsEnabled check.", true); + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] + public static partial void M0(global::Microsoft.Extensions.Logging.ILogger logger) + { + __M0Callback(logger, null); + } + } +} \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs index c9180b70f2320..33d1ab7c90171 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs @@ -178,6 +178,22 @@ public void MessageTests() Assert.Equal(string.Empty, logger.LastFormattedString); Assert.Equal(LogLevel.Debug, logger.LastLogLevel); Assert.Equal(1, logger.CallCount); + + logger.Reset(); + MessageTestExtensions.M5(logger, LogLevel.Trace); + Assert.Null(logger.LastException); + Assert.Equal(string.Empty, logger.LastFormattedString); + Assert.Equal(LogLevel.Trace, logger.LastLogLevel); + Assert.Equal(-1, logger.LastEventId.Id); + Assert.Equal(1, logger.CallCount); + + logger.Reset(); + MessageTestExtensions.M6(logger, LogLevel.Trace); + Assert.Null(logger.LastException); + Assert.Equal(string.Empty, logger.LastFormattedString); + Assert.Equal(LogLevel.Trace, logger.LastLogLevel); + Assert.Equal(6, logger.LastEventId.Id); + Assert.Equal(1, logger.CallCount); } [Fact] @@ -309,6 +325,14 @@ public void LevelTests() Assert.Equal("M9", logger.LastFormattedString); Assert.Equal(LogLevel.Trace, logger.LastLogLevel); Assert.Equal(1, logger.CallCount); + + logger.Reset(); + LevelTestExtensions.M10vs11(logger); + Assert.Null(logger.LastException); + Assert.Equal("event ID 10 vs. 11", logger.LastFormattedString); + Assert.Equal(LogLevel.Warning, logger.LastLogLevel); + Assert.Equal(1, logger.CallCount); + Assert.Equal(11, logger.LastEventId.Id); } [Fact] @@ -343,6 +367,40 @@ public void EventNameTests() Assert.Equal(LogLevel.Trace, logger.LastLogLevel); Assert.Equal(1, logger.CallCount); Assert.Equal("CustomEventName", logger.LastEventId.Name); + + logger.Reset(); + EventNameTestExtensions.CustomEventName(logger); + Assert.Null(logger.LastException); + Assert.Equal("CustomEventName", logger.LastFormattedString); + Assert.Equal(LogLevel.Trace, logger.LastLogLevel); + Assert.Equal(1, logger.CallCount); + Assert.Equal("CustomEventName", logger.LastEventId.Name); + } + + [Fact] + public void SkipEnabledCheckTests() + { + var logger = new MockLogger(); + + logger.Reset(); + logger.Enabled = false; + Assert.False(logger.IsEnabled(LogLevel.Information)); + SkipEnabledCheckExtensions.LoggerMethodWithFalseSkipEnabledCheck(logger); + Assert.Null(logger.LastException); + Assert.Null(logger.LastFormattedString); + Assert.Equal((LogLevel)(-1), logger.LastLogLevel); + Assert.Equal(0, logger.CallCount); + Assert.Equal(default, logger.LastEventId); + + logger.Reset(); + logger.Enabled = false; + Assert.False(logger.IsEnabled(LogLevel.Debug)); + SkipEnabledCheckExtensions.LoggerMethodWithTrueSkipEnabledCheck(logger); + Assert.Null(logger.LastException); + Assert.Equal("Message: When using SkipEnabledCheck, the generated code skips logger.IsEnabled(logLevel) check before calling log. To be used when consumer has already guarded logger method in an IsEnabled check.", logger.LastFormattedString); + Assert.Equal(LogLevel.Debug, logger.LastLogLevel); + Assert.Equal(1, logger.CallCount); + Assert.Equal("LoggerMethodWithTrueSkipEnabledCheck", logger.LastEventId.Name); } [Fact] diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs index 433d601c3d940..917d87cd127f1 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs @@ -35,17 +35,51 @@ public async Task TestEmitter() } [Fact] - public async Task TestBaseline_TestWithTwoParams_Success() + public async Task TestBaseline_TestWithSkipEnabledCheck_Success() { string testSourceCode = @" namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses { - internal static partial class TestWithTwoParams + internal static partial class TestWithSkipEnabledCheck { - [LoggerMessage(EventId = 0, Level = LogLevel.Error, Message = ""M0 {a1} {a2}"")] - public static partial void M0(ILogger logger, int a1, System.Collections.Generic.IEnumerable a2); + [LoggerMessage(EventId = 0, Level = LogLevel.Information, Message = ""Message: When using SkipEnabledCheck, the generated code skips logger.IsEnabled(logLevel) check before calling log. To be used when consumer has already guarded logger method in an IsEnabled check."", SkipEnabledCheck = true)] + public static partial void M0(ILogger logger); + } +}"; + await VerifyAgainstBaselineUsingFile("TestWithSkipEnabledCheck.generated.txt", testSourceCode); + } + + [Fact] + public async Task TestBaseline_TestWithDefaultValues_Success() + { + string testSourceCode = @" +namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses +{ + internal static partial class TestWithDefaultValues + { + [LoggerMessage] + public static partial void M0(ILogger logger, LogLevel level); } }"; + await VerifyAgainstBaselineUsingFile("TestWithDefaultValues.generated.txt", testSourceCode); + } + + [Theory] + [InlineData("EventId = 0, Level = LogLevel.Error, Message = \"M0 {a1} {a2}\"")] + [InlineData("eventId: 0, level: LogLevel.Error, message: \"M0 {a1} {a2}\"")] + [InlineData("0, LogLevel.Error, \"M0 {a1} {a2}\"")] + [InlineData("0, LogLevel.Error, \"M0 {a1} {a2}\", SkipEnabledCheck = false")] + public async Task TestBaseline_TestWithTwoParams_Success(string argumentList) + { + string testSourceCode = $@" +namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses +{{ + internal static partial class TestWithTwoParams + {{ + [LoggerMessage({argumentList})] + public static partial void M0(ILogger logger, int a1, System.Collections.Generic.IEnumerable a2); + }} +}}"; await VerifyAgainstBaselineUsingFile("TestWithTwoParams.generated.txt", testSourceCode); } diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs index 4991e416cad51..8e5df4972cf9a 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs @@ -64,6 +64,90 @@ static partial void M1(ILogger logger) Assert.Equal(DiagnosticDescriptors.LoggingMethodHasBody.Id, diagnostics[0].Id); } + [Theory] + [InlineData("EventId = 0, Level = null, Message = \"This is a message with {foo}\"")] + [InlineData("eventId: 0, level: null, message: \"This is a message with {foo}\"")] + [InlineData("0, null, \"This is a message with {foo}\"")] + public async Task WithNullLevel_GeneratorWontFail(string argumentList) + { + IReadOnlyList diagnostics = await RunGenerator($@" + partial class C + {{ + [LoggerMessage({argumentList})] + static partial void M1(ILogger logger, string foo); + + [LoggerMessage({argumentList})] + static partial void M2(ILogger logger, LogLevel level, string foo); + }} + "); + + Assert.Empty(diagnostics); + } + + [Theory] + [InlineData("EventId = null, Level = LogLevel.Debug, Message = \"This is a message with {foo}\"")] + [InlineData("eventId: null, level: LogLevel.Debug, message: \"This is a message with {foo}\"")] + [InlineData("null, LogLevel.Debug, \"This is a message with {foo}\"")] + public async Task WithNullEventId_GeneratorWontFail(string argumentList) + { + IReadOnlyList diagnostics = await RunGenerator($@" + partial class C + {{ + [LoggerMessage({argumentList})] + static partial void M1(ILogger logger, string foo); + }} + "); + + Assert.Empty(diagnostics); + } + + [Fact] + public async Task WithNullMessage_GeneratorWontFail() + { + IReadOnlyList diagnostics = await RunGenerator(@" + partial class C + { + [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = null)] + static partial void M1(ILogger logger, string foo); + } + "); + + Assert.Single(diagnostics); + Assert.Equal(DiagnosticDescriptors.ArgumentHasNoCorrespondingTemplate.Id, diagnostics[0].Id); + Assert.Contains("Argument 'foo' is not referenced from the logging message", diagnostics[0].GetMessage(), StringComparison.InvariantCulture); + } + + [Fact] + public async Task WithNullSkipEnabledCheck_GeneratorWontFail() + { + IReadOnlyList diagnostics = await RunGenerator(@" + partial class C + { + [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""This is a message with {foo}"", SkipEnabledCheck = null)] + static partial void M1(ILogger logger, string foo); + } + "); + + Assert.Empty(diagnostics); + } + + [Fact] + public async Task WithBadMisconfiguredInput_GeneratorWontFail() + { + IReadOnlyList diagnostics = await RunGenerator(@" + public static partial class C + { + [LoggerMessage(SkipEnabledCheck = 6)] + public static partial void M0(ILogger logger, LogLevel level); + + [LoggerMessage(eventId: true, level: LogLevel.Debug, message: ""misconfigured eventId as bool"")] + public static partial void M1(ILogger logger); + } + "); + + Assert.Empty(diagnostics); + } + [Fact] public async Task MissingTemplate() { @@ -266,6 +350,26 @@ public partial class Nested Assert.Empty(diagnostics); } + [Theory] + [InlineData("false")] + [InlineData("true")] + [InlineData("null")] + public async Task UsingSkipEnabledCheck(string skipEnabledCheckValue) + { + IReadOnlyList diagnostics = await RunGenerator($@" + partial class C + {{ + public partial class WithLoggerMethodUsingSkipEnabledCheck + {{ + [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"", SkipEnabledCheck = {skipEnabledCheckValue})] + static partial void M1(ILogger logger); + }} + }} + "); + + Assert.Empty(diagnostics); + } + [Fact] public async Task MissingExceptionType() { diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EventNameTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EventNameTestExtensions.cs index 4c0ddf320aabf..f41d615d04314 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EventNameTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EventNameTestExtensions.cs @@ -7,5 +7,8 @@ internal static partial class EventNameTestExtensions { [LoggerMessage(EventId = 0, Level = LogLevel.Trace, Message = "M0", EventName = "CustomEventName")] public static partial void M0(ILogger logger); + + [LoggerMessage(EventId = 2, Level = LogLevel.Trace, Message = "CustomEventName")] // EventName inferred from method name + public static partial void CustomEventName(ILogger logger); } } diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/LevelTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/LevelTestExtensions.cs index 5726aa02a4c3f..2251d198a2af2 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/LevelTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/LevelTestExtensions.cs @@ -34,5 +34,8 @@ internal static partial class LevelTestExtensions [LoggerMessage(EventId = 9, Message = "M9")] public static partial void M9(LogLevel level, ILogger logger); + + [LoggerMessage(eventId: 10, level: LogLevel.Warning, message: "event ID 10 vs. 11", EventId = 11)] + public static partial void M10vs11(ILogger logger); } } diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs index a30849288b53b..8cad8db64cd9d 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs @@ -29,5 +29,11 @@ internal static partial class MessageTestExtensions [LoggerMessage(EventId = 4, Level = LogLevel.Debug, Message = "{p1}")] public static partial void M4(ILogger logger, string p1, int p2, int p3); #endif + + [LoggerMessage] + public static partial void M5(ILogger logger, LogLevel level); + + [LoggerMessage(EventId = 6, Message = "")] + public static partial void M6(ILogger logger, LogLevel level); } } diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/SkipEnabledCheckExtensions.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/SkipEnabledCheckExtensions.cs new file mode 100644 index 0000000000000..397acdf080ca4 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/SkipEnabledCheckExtensions.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. + +namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses +{ + internal static partial class SkipEnabledCheckExtensions + { + [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = "Message: When using SkipEnabledCheck, the generated code skips logger.IsEnabled(logLevel) check before calling log. To be used when consumer has already guarded logger method in an IsEnabled check.", SkipEnabledCheck = true)] + internal static partial void LoggerMethodWithTrueSkipEnabledCheck(ILogger logger); + + [LoggerMessage(EventId = 1, Level = LogLevel.Information, Message = "M1", SkipEnabledCheck = false)] + internal static partial void LoggerMethodWithFalseSkipEnabledCheck(ILogger logger); + } +} \ No newline at end of file From ac106e87e1224bc81f28d82f1f1507dfd65ad975 Mon Sep 17 00:00:00 2001 From: Anton Lapounov Date: Tue, 22 Jun 2021 15:45:36 -0700 Subject: [PATCH 069/107] Set DLL flag on R2R binaries (#54533) This is required for R2R relocations to be processed by the OS loader on Windows 7. --- .../CodeGen/ReadyToRunObjectWriter.cs | 8 +--- .../ObjectWriter/R2RPEBuilder.cs | 48 +++++-------------- 2 files changed, 13 insertions(+), 43 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs index e54207ff00fb1..74da51246b620 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs @@ -198,11 +198,7 @@ public void EmitPortableExecutable() if (_nodeFactory.CompilationModuleGroup.IsCompositeBuildMode && _componentModule == null) { - headerBuilder = PEHeaderProvider.Create( - imageCharacteristics: Characteristics.ExecutableImage | Characteristics.Dll, - dllCharacteristics: default(DllCharacteristics), - Subsystem.Unknown, - _nodeFactory.Target); + headerBuilder = PEHeaderProvider.Create(Subsystem.Unknown, _nodeFactory.Target); peIdProvider = new Func, BlobContentId>(content => BlobContentId.FromHash(CryptographicHashProvider.ComputeSourceHash(content))); timeDateStamp = null; r2rHeaderExportSymbol = _nodeFactory.Header; @@ -210,7 +206,7 @@ public void EmitPortableExecutable() else { PEReader inputPeReader = (_componentModule != null ? _componentModule.PEReader : _nodeFactory.CompilationModuleGroup.CompilationModuleSet.First().PEReader); - headerBuilder = PEHeaderProvider.Copy(inputPeReader.PEHeaders, _nodeFactory.Target); + headerBuilder = PEHeaderProvider.Create(inputPeReader.PEHeaders.PEHeader.Subsystem, _nodeFactory.Target); timeDateStamp = inputPeReader.PEHeaders.CoffHeader.TimeDateStamp; r2rHeaderExportSymbol = null; } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs index 3f6764b0e804b..c7212368ace07 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs @@ -489,7 +489,7 @@ private void SetPEHeaderTimeStamp(Stream outputStream, int timeDateStamp) DosHeaderSize + PESignatureSize + sizeof(short) + // Machine - sizeof(short); //NumberOfSections + sizeof(short); // NumberOfSections outputStream.Seek(seekSize, SeekOrigin.Begin); outputStream.Write(patchedTimestamp, 0, patchedTimestamp.Length); @@ -664,52 +664,28 @@ protected override BlobBuilder SerializeSection(string name, SectionLocation loc } /// - /// Simple helper for filling in PE header information by either copying over - /// data from a pre-existing input PE header (used for single-assembly R2R files) - /// or by explicitly specifying the image characteristics (for composite R2R). + /// Simple helper for filling in PE header information. /// static class PEHeaderProvider { - /// - /// Copy PE headers into a PEHeaderBuilder used by PEBuilder. - /// - /// Headers to copy - /// Target architecture to set in the header - public static PEHeaderBuilder Copy(PEHeaders peHeaders, TargetDetails target) - { - return Create( - peHeaders.CoffHeader.Characteristics, - peHeaders.PEHeader.DllCharacteristics, - peHeaders.PEHeader.Subsystem, - target); - } - /// /// Fill in PE header information into a PEHeaderBuilder used by PEBuilder. /// - /// Relocs are not present in the PE executable - /// Extra DLL characteristics to apply /// Targeting subsystem /// Target architecture to set in the header - public static PEHeaderBuilder Create(Characteristics imageCharacteristics, DllCharacteristics dllCharacteristics, Subsystem subsystem, TargetDetails target) + public static PEHeaderBuilder Create(Subsystem subsystem, TargetDetails target) { bool is64BitTarget = target.PointerSize == sizeof(long); - imageCharacteristics &= ~(Characteristics.Bit32Machine | Characteristics.LargeAddressAware); - imageCharacteristics |= (is64BitTarget ? Characteristics.LargeAddressAware : Characteristics.Bit32Machine); + Characteristics imageCharacteristics = Characteristics.ExecutableImage | Characteristics.Dll; + imageCharacteristics |= is64BitTarget ? Characteristics.LargeAddressAware : Characteristics.Bit32Machine; - ulong imageBase = PE32HeaderConstants.ImageBase; - if (target.IsWindows && is64BitTarget && (imageBase <= uint.MaxValue)) - { - // Base addresses below 4 GiB are reserved for WoW on x64 and disallowed on ARM64. - // If the input assembly was compiled for anycpu, its base address is 32-bit and we need to fix it. - imageBase = (imageCharacteristics & Characteristics.Dll) != 0 ? PE64HeaderConstants.DllImageBase : PE64HeaderConstants.ExeImageBase; - } + ulong imageBase = is64BitTarget ? PE64HeaderConstants.DllImageBase : PE32HeaderConstants.ImageBase; int fileAlignment = 0x200; if (!target.IsWindows && !is64BitTarget) { - // To minimize wasted VA space on 32 bit systems align file to page bounaries (presumed to be 4K). + // To minimize wasted VA space on 32-bit systems, align file to page boundaries (presumed to be 4K) fileAlignment = 0x1000; } @@ -721,13 +697,11 @@ public static PEHeaderBuilder Create(Characteristics imageCharacteristics, DllCh sectionAlignment = fileAlignment; } - dllCharacteristics &= DllCharacteristics.AppContainer; - - // In Crossgen1, this is under a debug-specific condition 'if (0 == CLRConfig::GetConfigValue(CLRConfig::INTERNAL_NoASLRForNgen))' - dllCharacteristics |= DllCharacteristics.DynamicBase; - // Without NxCompatible the PE executable cannot execute on Windows ARM64 - dllCharacteristics |= DllCharacteristics.NxCompatible | DllCharacteristics.TerminalServerAware; + DllCharacteristics dllCharacteristics = + DllCharacteristics.DynamicBase | + DllCharacteristics.NxCompatible | + DllCharacteristics.TerminalServerAware; if (is64BitTarget) { From 1f5033c333370334286861368205293abb0abee6 Mon Sep 17 00:00:00 2001 From: Dan Moseley Date: Tue, 22 Jun 2021 19:08:12 -0600 Subject: [PATCH 070/107] Disable another failing MemoryCache test (#54578) --- .../tests/MemoryCacheSetAndRemoveTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/Microsoft.Extensions.Caching.Memory/tests/MemoryCacheSetAndRemoveTests.cs b/src/libraries/Microsoft.Extensions.Caching.Memory/tests/MemoryCacheSetAndRemoveTests.cs index e9e7fb4ebfc55..fb15d9732061e 100644 --- a/src/libraries/Microsoft.Extensions.Caching.Memory/tests/MemoryCacheSetAndRemoveTests.cs +++ b/src/libraries/Microsoft.Extensions.Caching.Memory/tests/MemoryCacheSetAndRemoveTests.cs @@ -594,6 +594,7 @@ public void OvercapacityPurge_AreThreadSafe() } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/33993")] public void AddAndReplaceEntries_AreThreadSafe() { var cache = new MemoryCache(new MemoryCacheOptions From 9701ef9e6cf8844e2fb0452177affc1e70e1c108 Mon Sep 17 00:00:00 2001 From: Sergey Andreenko Date: Tue, 22 Jun 2021 18:58:23 -0700 Subject: [PATCH 071/107] Keep obj node for ArrayIndex. (#54584) --- src/coreclr/jit/morph.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 2d0d108a3349c..6d7f8c7bc6fdc 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -5509,8 +5509,16 @@ GenTree* Compiler::fgMorphArrayIndex(GenTree* tree) fgSetRngChkTarget(indexAddr); } - // Change `tree` into an indirection and return. - tree->ChangeOper(GT_IND); + if (!tree->TypeIs(TYP_STRUCT)) + { + tree->ChangeOper(GT_IND); + } + else + { + DEBUG_DESTROY_NODE(tree); + tree = gtNewObjNode(elemStructType, indexAddr); + INDEBUG(tree->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); + } GenTreeIndir* const indir = tree->AsIndir(); indir->Addr() = indexAddr; bool canCSE = indir->CanCSE(); @@ -5520,9 +5528,7 @@ GenTree* Compiler::fgMorphArrayIndex(GenTree* tree) indir->SetDoNotCSE(); } -#ifdef DEBUG - indexAddr->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED; -#endif // DEBUG + INDEBUG(indexAddr->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); return indir; } From 8026e74e3d7c45f34dbea1da9dd25ff394f1afd6 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Tue, 22 Jun 2021 23:46:33 -0400 Subject: [PATCH 072/107] [wasm] Move AOT builds from `runtime-staging` to `runtime` (#54577) These builds have had ~2-3 failures in the last 14 days (~90 builds). --- eng/pipelines/runtime-staging.yml | 42 ------------------------------- eng/pipelines/runtime.yml | 42 +++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/eng/pipelines/runtime-staging.yml b/eng/pipelines/runtime-staging.yml index 36ff5c79c00ed..2352995df910a 100644 --- a/eng/pipelines/runtime-staging.yml +++ b/eng/pipelines/runtime-staging.yml @@ -256,48 +256,6 @@ jobs: eq(variables['monoContainsChange'], true), eq(variables['isFullMatrix'], true)) -# -# Build the whole product using Mono and run libraries tests -# -- template: /eng/pipelines/common/platform-matrix.yml - parameters: - jobTemplate: /eng/pipelines/common/global-build-job.yml - helixQueuesTemplate: /eng/pipelines/libraries/helix-queues-setup.yml - buildConfig: Release - runtimeFlavor: mono - platforms: - - Browser_wasm - variables: - # map dependencies variables to local variables - - name: librariesContainsChange - value: $[ dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'] ] - - name: monoContainsChange - value: $[ dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'] ] - jobParameters: - testGroup: innerloop - nameSuffix: AllSubsets_Mono_AOT - buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:EnableAggressiveTrimming=true /p:BuildAOTTestsOnHelix=true /p:RunAOTCompilation=true - timeoutInMinutes: 180 - condition: >- - or( - eq(dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'], true), - eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true), - eq(dependencies.evaluate_paths.outputs['SetPathVars_installer.containsChange'], true), - eq(variables['isFullMatrix'], true)) - # extra steps, run tests - extraStepsTemplate: /eng/pipelines/libraries/helix.yml - extraStepsParameters: - creator: dotnet-bot - testRunNamePrefixSuffix: Mono_$(_BuildConfig) - extraHelixArguments: /p:NeedsToBuildWasmAppsOnHelix=true - scenarios: - - normal - condition: >- - or( - eq(variables['librariesContainsChange'], true), - eq(variables['monoContainsChange'], true), - eq(variables['isFullMatrix'], true)) - # # Build the whole product using Mono for Android and run runtime tests with interpreter # diff --git a/eng/pipelines/runtime.yml b/eng/pipelines/runtime.yml index 148566ea7a262..931a65a05e11e 100644 --- a/eng/pipelines/runtime.yml +++ b/eng/pipelines/runtime.yml @@ -347,6 +347,48 @@ jobs: eq(variables['monoContainsChange'], true), eq(variables['isFullMatrix'], true)) +# +# Build for Browser/wasm with RunAOTCompilation=true +# +- template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/common/global-build-job.yml + helixQueuesTemplate: /eng/pipelines/libraries/helix-queues-setup.yml + buildConfig: Release + runtimeFlavor: mono + platforms: + - Browser_wasm + variables: + # map dependencies variables to local variables + - name: librariesContainsChange + value: $[ dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'] ] + - name: monoContainsChange + value: $[ dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'] ] + jobParameters: + testGroup: innerloop + nameSuffix: AllSubsets_Mono_AOT + buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:EnableAggressiveTrimming=true /p:BuildAOTTestsOnHelix=true /p:RunAOTCompilation=true + timeoutInMinutes: 180 + condition: >- + or( + eq(dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'], true), + eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true), + eq(dependencies.evaluate_paths.outputs['SetPathVars_installer.containsChange'], true), + eq(variables['isFullMatrix'], true)) + # extra steps, run tests + extraStepsTemplate: /eng/pipelines/libraries/helix.yml + extraStepsParameters: + creator: dotnet-bot + testRunNamePrefixSuffix: Mono_$(_BuildConfig) + extraHelixArguments: /p:NeedsToBuildWasmAppsOnHelix=true + scenarios: + - normal + condition: >- + or( + eq(variables['librariesContainsChange'], true), + eq(variables['monoContainsChange'], true), + eq(variables['isFullMatrix'], true)) + # Build and test libraries under single-file publishing - template: /eng/pipelines/common/platform-matrix.yml parameters: From f3556b35c66198ac054bb51bc29820d6a83d7c4f Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Tue, 22 Jun 2021 21:42:14 -0700 Subject: [PATCH 073/107] Disable failing System.Reflection.Tests.ModuleTests.GetMethods (#54564) Tracking: https://github.com/dotnet/runtime/issues/50831 --- .../System.Runtime/tests/System/Reflection/ModuleTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/System.Runtime/tests/System/Reflection/ModuleTests.cs b/src/libraries/System.Runtime/tests/System/Reflection/ModuleTests.cs index a481ee253a55e..29fe532e6c0eb 100644 --- a/src/libraries/System.Runtime/tests/System/Reflection/ModuleTests.cs +++ b/src/libraries/System.Runtime/tests/System/Reflection/ModuleTests.cs @@ -232,6 +232,7 @@ public void GetMethod() [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/51912", typeof(PlatformDetection), nameof(PlatformDetection.IsBuiltWithAggressiveTrimming), nameof(PlatformDetection.IsBrowser))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/50831")] public void GetMethods() { var methodNames = TestModule.GetMethods().Select(m => m.Name).ToArray(); From d87b78342c5d88741eb1f194b4c9968baf192ddc Mon Sep 17 00:00:00 2001 From: Daniel Genkin Date: Wed, 23 Jun 2021 00:46:38 -0400 Subject: [PATCH 074/107] Added runtime dependency to fix the intermittent test failures (#54587) * Added runtime dependency to hopefully fix the intermittent test failures * addressed comments * cleanup * cleanup accidental spaces and tabs cleanup * added Larry's comments --- src/mono/wasm/runtime/library_mono.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/mono/wasm/runtime/library_mono.js b/src/mono/wasm/runtime/library_mono.js index 36837f62cf709..e9cb8cd2b401d 100644 --- a/src/mono/wasm/runtime/library_mono.js +++ b/src/mono/wasm/runtime/library_mono.js @@ -2365,12 +2365,14 @@ var MonoSupportLib = { }, /** - * Loads the mono config file (typically called mono-config.json) + * Loads the mono config file (typically called mono-config.json) asynchroniously + * Note: the run dependencies are so emsdk actually awaits it in order. * * @param {string} configFilePath - relative path to the config file * @throws Will throw an error if the config file loading fails */ - mono_wasm_load_config: async function (configFilePath) { + mono_wasm_load_config: async function (configFilePath) { + Module.addRunDependency(configFilePath); try { let config = null; // NOTE: when we add nodejs make sure to include the nodejs fetch package @@ -2385,6 +2387,8 @@ var MonoSupportLib = { return config; } catch(e) { return {message: "failed to load config file", error: e}; + } finally { + Module.removeRunDependency(configFilePath); } } }, From cd0b3ef6695e520e8c4757fed34891e6860dd7bd Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 23 Jun 2021 02:34:38 -0400 Subject: [PATCH 075/107] Update NetAnalyzers version (#54511) * Update NetAnalyzers version * Add NetAnalyzers to dependency flow Co-authored-by: Viktor Hofer --- eng/CodeAnalysis.ruleset | 1 + eng/Version.Details.xml | 4 ++++ eng/Versions.props | 3 ++- .../src/System/Data/Common/DbConnectionOptions.Common.cs | 3 +-- .../src/Microsoft/Win32/RegistryKey.cs | 3 +-- .../src/System.Configuration.ConfigurationManager.csproj | 1 + .../src/System.Diagnostics.EventLog.csproj | 2 +- .../src/System.Diagnostics.PerformanceCounter.csproj | 1 + .../src/System/IO/DriveInfo.UnixOrBrowser.cs | 2 +- src/libraries/System.Speech/src/System.Speech.csproj | 2 +- src/tasks/AndroidAppBuilder/ApkBuilder.cs | 2 +- 11 files changed, 15 insertions(+), 9 deletions(-) diff --git a/eng/CodeAnalysis.ruleset b/eng/CodeAnalysis.ruleset index 4631414599589..8faa50e9a6f00 100644 --- a/eng/CodeAnalysis.ruleset +++ b/eng/CodeAnalysis.ruleset @@ -126,6 +126,7 @@ + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 99fb28cb6e9bd..5c1f457ae40f4 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -219,5 +219,9 @@ https://github.com/dotnet/runtime-assets 8d7b898b96cbdb868cac343e938173105287ed9e + + https://github.com/dotnet/roslyn-analyzers + fcddb771f42866f9521f23f093b1f30e129018bb + diff --git a/eng/Versions.props b/eng/Versions.props index b62b9b4854cfc..2295b6e440d51 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -45,9 +45,10 @@ 3.8.0 - 6.0.0-preview6.21281.1 + 3.10.0-2.final 3.10.0-2.final + 6.0.0-rc1.21320.2 6.0.0-beta.21311.3 6.0.0-beta.21311.3 diff --git a/src/libraries/Common/src/System/Data/Common/DbConnectionOptions.Common.cs b/src/libraries/Common/src/System/Data/Common/DbConnectionOptions.Common.cs index fda8cc871d916..80a092104ae50 100644 --- a/src/libraries/Common/src/System/Data/Common/DbConnectionOptions.Common.cs +++ b/src/libraries/Common/src/System/Data/Common/DbConnectionOptions.Common.cs @@ -406,8 +406,7 @@ private static bool IsValueValidInternal(string? keyvalue) bool compValue = s_connectionStringValidValueRegex.IsMatch(keyvalue); Debug.Assert((-1 == keyvalue.IndexOf('\u0000')) == compValue, "IsValueValid mismatch with regex"); #endif - // string.Contains(char) is .NetCore2.1+ specific - return (-1 == keyvalue.IndexOf('\u0000')); + return (-1 == keyvalue.IndexOf('\u0000')); // string.Contains(char) is .NetCore2.1+ specific } return true; } diff --git a/src/libraries/Microsoft.Win32.Registry/src/Microsoft/Win32/RegistryKey.cs b/src/libraries/Microsoft.Win32.Registry/src/Microsoft/Win32/RegistryKey.cs index 54054d809c284..ea5a83315216d 100644 --- a/src/libraries/Microsoft.Win32.Registry/src/Microsoft/Win32/RegistryKey.cs +++ b/src/libraries/Microsoft.Win32.Registry/src/Microsoft/Win32/RegistryKey.cs @@ -628,8 +628,7 @@ private static string FixupName(string name) { Debug.Assert(name != null, "[FixupName]name!=null"); - // string.Contains(char) is .NetCore2.1+ specific - if (name.IndexOf('\\') == -1) + if (!name.Contains('\\')) { return name; } diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System.Configuration.ConfigurationManager.csproj b/src/libraries/System.Configuration.ConfigurationManager/src/System.Configuration.ConfigurationManager.csproj index 104386de5edba..209d36e49c51b 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System.Configuration.ConfigurationManager.csproj +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System.Configuration.ConfigurationManager.csproj @@ -3,6 +3,7 @@ $(NetCoreAppCurrent);netstandard2.0;net461 false + $(NoWarn);CA1847 diff --git a/src/libraries/System.Diagnostics.EventLog/src/System.Diagnostics.EventLog.csproj b/src/libraries/System.Diagnostics.EventLog/src/System.Diagnostics.EventLog.csproj index 14a4d33561620..a093eb35ac7fb 100644 --- a/src/libraries/System.Diagnostics.EventLog/src/System.Diagnostics.EventLog.csproj +++ b/src/libraries/System.Diagnostics.EventLog/src/System.Diagnostics.EventLog.csproj @@ -2,7 +2,7 @@ true $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent);netcoreapp3.1-windows;netcoreapp3.1;netstandard2.0;net461 - $(NoWarn);CA1838 + $(NoWarn);CA1838;CA1847 diff --git a/src/libraries/System.Diagnostics.PerformanceCounter/src/System.Diagnostics.PerformanceCounter.csproj b/src/libraries/System.Diagnostics.PerformanceCounter/src/System.Diagnostics.PerformanceCounter.csproj index f66b352c9c186..600f6fc58b993 100644 --- a/src/libraries/System.Diagnostics.PerformanceCounter/src/System.Diagnostics.PerformanceCounter.csproj +++ b/src/libraries/System.Diagnostics.PerformanceCounter/src/System.Diagnostics.PerformanceCounter.csproj @@ -2,6 +2,7 @@ true $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent);netcoreapp3.1-windows;netcoreapp3.1;netstandard2.0;net461 + $(NoWarn);CA1847 diff --git a/src/libraries/System.IO.FileSystem.DriveInfo/src/System/IO/DriveInfo.UnixOrBrowser.cs b/src/libraries/System.IO.FileSystem.DriveInfo/src/System/IO/DriveInfo.UnixOrBrowser.cs index 51892986a5df6..b39b223228834 100644 --- a/src/libraries/System.IO.FileSystem.DriveInfo/src/System/IO/DriveInfo.UnixOrBrowser.cs +++ b/src/libraries/System.IO.FileSystem.DriveInfo/src/System/IO/DriveInfo.UnixOrBrowser.cs @@ -24,7 +24,7 @@ public static DriveInfo[] GetDrives() private static string NormalizeDriveName(string driveName) { - if (driveName.Contains("\0")) // string.Contains(char) is .NetCore2.1+ specific + if (driveName.Contains('\0')) { throw new ArgumentException(SR.Format(SR.Arg_InvalidDriveChars, driveName), nameof(driveName)); } diff --git a/src/libraries/System.Speech/src/System.Speech.csproj b/src/libraries/System.Speech/src/System.Speech.csproj index a38b451935628..8493864d2f6d6 100644 --- a/src/libraries/System.Speech/src/System.Speech.csproj +++ b/src/libraries/System.Speech/src/System.Speech.csproj @@ -4,7 +4,7 @@ $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent);netcoreapp3.1-windows;netcoreapp3.1;netstandard2.0 - $(NoWarn);CS0649;SA1129 + $(NoWarn);CS0649;SA1129;CA1847 false diff --git a/src/tasks/AndroidAppBuilder/ApkBuilder.cs b/src/tasks/AndroidAppBuilder/ApkBuilder.cs index 346f854c383e6..b979ba316b33d 100644 --- a/src/tasks/AndroidAppBuilder/ApkBuilder.cs +++ b/src/tasks/AndroidAppBuilder/ApkBuilder.cs @@ -495,7 +495,7 @@ private static string GetLatestBuildTools(string androidSdkDir) { string? buildTools = Directory.GetDirectories(Path.Combine(androidSdkDir, "build-tools")) .Select(Path.GetFileName) - .Where(file => !file!.Contains("-")) + .Where(file => !file!.Contains('-')) .Select(file => { Version.TryParse(Path.GetFileName(file), out Version? version); return version; }) .OrderByDescending(v => v) .FirstOrDefault()?.ToString(); From 0aafceb47a2a26783e3b3ece891e45bf49354fe6 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Wed, 23 Jun 2021 08:53:55 +0200 Subject: [PATCH 076/107] [FileStream] handle UNC and device paths (#54483) * stop using NtCreateFile as there is no public and reliable way of mapping DOS to NT paths --- .../Kernel32/Interop.FILE_ALLOCATION_INFO.cs | 16 +++ .../SafeHandles/SafeFileHandle.Windows.cs | 127 ++++++++++++------ .../System.Private.CoreLib.Shared.projitems | 6 + 3 files changed, 106 insertions(+), 43 deletions(-) create mode 100644 src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FILE_ALLOCATION_INFO.cs diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FILE_ALLOCATION_INFO.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FILE_ALLOCATION_INFO.cs new file mode 100644 index 0000000000000..0233626ee73c6 --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FILE_ALLOCATION_INFO.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +internal static partial class Interop +{ + internal static partial class Kernel32 + { + // Value taken from https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-setfileinformationbyhandle#remarks: + internal const int FileAllocationInfo = 5; + + internal struct FILE_ALLOCATION_INFO + { + internal long AllocationSize; + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs index 36710dcdbde5d..5aa7a2c7f4dd6 100644 --- a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs @@ -4,7 +4,8 @@ using System; using System.Diagnostics; using System.IO; -using System.Text; +using System.IO.Strategies; +using System.Runtime.InteropServices; using System.Threading; namespace Microsoft.Win32.SafeHandles @@ -24,13 +25,6 @@ public SafeFileHandle(IntPtr preexistingHandle, bool ownsHandle) : base(ownsHand SetHandle(preexistingHandle); } - private SafeFileHandle(IntPtr preexistingHandle, bool ownsHandle, FileOptions fileOptions) : base(ownsHandle) - { - SetHandle(preexistingHandle); - - _fileOptions = fileOptions; - } - public bool IsAsync => (GetFileOptions() & FileOptions.Asynchronous) != 0; internal bool CanSeek => !IsClosed && GetFileType() == Interop.Kernel32.FileTypes.FILE_TYPE_DISK; @@ -43,10 +37,14 @@ internal static unsafe SafeFileHandle Open(string fullPath, FileMode mode, FileA { using (DisableMediaInsertionPrompt.Create()) { - SafeFileHandle fileHandle = new SafeFileHandle( - NtCreateFile(fullPath, mode, access, share, options, preallocationSize), - ownsHandle: true, - options); + // we don't use NtCreateFile as there is no public and reliable way + // of converting DOS to NT file paths (RtlDosPathNameToRelativeNtPathName_U_WithStatus is not documented) + SafeFileHandle fileHandle = CreateFile(fullPath, mode, access, share, options); + + if (FileStreamHelpers.ShouldPreallocate(preallocationSize, access, mode)) + { + Preallocate(fullPath, preallocationSize, fileHandle); + } fileHandle.InitThreadPoolBindingIfNeeded(); @@ -54,48 +52,91 @@ internal static unsafe SafeFileHandle Open(string fullPath, FileMode mode, FileA } } - private static IntPtr NtCreateFile(string fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options, long preallocationSize) + private static unsafe SafeFileHandle CreateFile(string fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options) { - uint ntStatus; - IntPtr fileHandle; + Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = default; + if ((share & FileShare.Inheritable) != 0) + { + secAttrs = new Interop.Kernel32.SECURITY_ATTRIBUTES + { + nLength = (uint)sizeof(Interop.Kernel32.SECURITY_ATTRIBUTES), + bInheritHandle = Interop.BOOL.TRUE + }; + } - const string MandatoryNtPrefix = @"\??\"; - if (fullPath.StartsWith(MandatoryNtPrefix, StringComparison.Ordinal)) + int fAccess = + ((access & FileAccess.Read) == FileAccess.Read ? Interop.Kernel32.GenericOperations.GENERIC_READ : 0) | + ((access & FileAccess.Write) == FileAccess.Write ? Interop.Kernel32.GenericOperations.GENERIC_WRITE : 0); + + // Our Inheritable bit was stolen from Windows, but should be set in + // the security attributes class. Don't leave this bit set. + share &= ~FileShare.Inheritable; + + // Must use a valid Win32 constant here... + if (mode == FileMode.Append) { - (ntStatus, fileHandle) = Interop.NtDll.NtCreateFile(fullPath, mode, access, share, options, preallocationSize); + mode = FileMode.OpenOrCreate; } - else + + int flagsAndAttributes = (int)options; + + // For mitigating local elevation of privilege attack through named pipes + // make sure we always call CreateFile with SECURITY_ANONYMOUS so that the + // named pipe server can't impersonate a high privileged client security context + // (note that this is the effective default on CreateFile2) + flagsAndAttributes |= (Interop.Kernel32.SecurityOptions.SECURITY_SQOS_PRESENT | Interop.Kernel32.SecurityOptions.SECURITY_ANONYMOUS); + + SafeFileHandle fileHandle = Interop.Kernel32.CreateFile(fullPath, fAccess, share, &secAttrs, mode, flagsAndAttributes, IntPtr.Zero); + if (fileHandle.IsInvalid) { - var vsb = new ValueStringBuilder(stackalloc char[256]); - vsb.Append(MandatoryNtPrefix); + // Return a meaningful exception with the full path. - if (fullPath.StartsWith(@"\\?\", StringComparison.Ordinal)) // NtCreateFile does not support "\\?\" prefix, only "\??\" - { - vsb.Append(fullPath.AsSpan(4)); - } - else + // NT5 oddity - when trying to open "C:\" as a Win32FileStream, + // we usually get ERROR_PATH_NOT_FOUND from the OS. We should + // probably be consistent w/ every other directory. + int errorCode = Marshal.GetLastPInvokeError(); + + if (errorCode == Interop.Errors.ERROR_PATH_NOT_FOUND && fullPath!.Length == PathInternal.GetRootLength(fullPath)) { - vsb.Append(fullPath); + errorCode = Interop.Errors.ERROR_ACCESS_DENIED; } - (ntStatus, fileHandle) = Interop.NtDll.NtCreateFile(vsb.AsSpan(), mode, access, share, options, preallocationSize); - vsb.Dispose(); + throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath); } - switch (ntStatus) - { - case Interop.StatusOptions.STATUS_SUCCESS: - return fileHandle; - case Interop.StatusOptions.STATUS_DISK_FULL: - throw new IOException(SR.Format(SR.IO_DiskFull_Path_AllocationSize, fullPath, preallocationSize)); - // NtCreateFile has a bug and it reports STATUS_INVALID_PARAMETER for files - // that are too big for the current file system. Example: creating a 4GB+1 file on a FAT32 drive. - case Interop.StatusOptions.STATUS_INVALID_PARAMETER when preallocationSize > 0: - case Interop.StatusOptions.STATUS_FILE_TOO_LARGE: - throw new IOException(SR.Format(SR.IO_FileTooLarge_Path_AllocationSize, fullPath, preallocationSize)); - default: - int error = (int)Interop.NtDll.RtlNtStatusToDosError((int)ntStatus); - throw Win32Marshal.GetExceptionForWin32Error(error, fullPath); + fileHandle._fileOptions = options; + return fileHandle; + } + + private static unsafe void Preallocate(string fullPath, long preallocationSize, SafeFileHandle fileHandle) + { + var allocationInfo = new Interop.Kernel32.FILE_ALLOCATION_INFO + { + AllocationSize = preallocationSize + }; + + if (!Interop.Kernel32.SetFileInformationByHandle( + fileHandle, + Interop.Kernel32.FileAllocationInfo, + &allocationInfo, + (uint)sizeof(Interop.Kernel32.FILE_ALLOCATION_INFO))) + { + int errorCode = Marshal.GetLastPInvokeError(); + + // we try to mimic the atomic NtCreateFile here: + // if preallocation fails, close the handle and delete the file + fileHandle.Dispose(); + Interop.Kernel32.DeleteFile(fullPath); + + switch (errorCode) + { + case Interop.Errors.ERROR_DISK_FULL: + throw new IOException(SR.Format(SR.IO_DiskFull_Path_AllocationSize, fullPath, preallocationSize)); + case Interop.Errors.ERROR_FILE_TOO_LARGE: + throw new IOException(SR.Format(SR.IO_FileTooLarge_Path_AllocationSize, fullPath, preallocationSize)); + default: + throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath); + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index a7ce085d9a065..2af41ff13424b 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 @@ -1426,6 +1426,9 @@ Common\Interop\Windows\Kernel32\Interop.FILE_BASIC_INFO.cs + + Common\Interop\Windows\Kernel32\Interop.FILE_ALLOCATION_INFO.cs + Common\Interop\Windows\Kernel32\Interop.FILE_END_OF_FILE_INFO.cs @@ -1612,6 +1615,9 @@ Common\Interop\Windows\Interop.UNICODE_STRING.cs + + Common\Interop\Windows\Kernel32\Interop.SecurityOptions.cs + Common\Interop\Windows\Interop.SECURITY_QUALITY_OF_SERVICE.cs From d783a8c812b387a9b8befa0ec5172857c04d845c Mon Sep 17 00:00:00 2001 From: Noah Falk Date: Wed, 23 Jun 2021 00:00:47 -0700 Subject: [PATCH 077/107] Update library testing docs page to reduce confusion (#54324) * Add warning on unmaintained testing doc page * Update testing.md Some example text that seems more clear to me, but only offered as a suggestion. Feel free to adjust it or if you want to use it as-is please double check what I wrote is accurate : ) I think the useful elements are: 1. Being explicit about what workflow steps need to happen in total 2. Being explicit about which commands are covering the entire workflow and which ones are only covering a part of it 3. Show the simple "do-it-all" options first before showing more complex partial options. Glancing at the first example and blindly copying it should land in the pit of success. Co-authored-by: Viktor Hofer --- docs/workflow/testing/libraries/testing.md | 65 ++++++++++++++-------- 1 file changed, 41 insertions(+), 24 deletions(-) diff --git a/docs/workflow/testing/libraries/testing.md b/docs/workflow/testing/libraries/testing.md index fe4d76255a791..a78a3d0409868 100644 --- a/docs/workflow/testing/libraries/testing.md +++ b/docs/workflow/testing/libraries/testing.md @@ -1,54 +1,71 @@ # Testing Libraries -We use the OSS testing framework [xunit](https://github.com/xunit/xunit). +## Full Build and Test Run -To build the tests and run them you can call the libraries build script. For libraries tests to work, you must have built the coreclr or mono runtime for them to run on. +These example commands automate the test run and all pre-requisite build steps in a single command from a clean enlistment. -**Examples** -- The following shows how to build only the tests but not run them: +- Run all tests - Builds clr in release, libs+tests in debug: ``` -build.cmd/sh -subset libs.tests +build.cmd/sh -subset clr+libs+libs.tests -test -rc Release ``` -- The following builds and runs all tests using clr: +- Run all tests - Builds Mono in release, libs+tests in debug: ``` -build.cmd/sh -subset clr+libs.tests -test +build.cmd/sh -subset mono+libs+libs.tests -test -rc Release ``` -- The following builds and runs all tests using mono: +- Run all tests - Build Mono and libs for x86 architecture in debug (choosing debug for runtime will run very slowly): ``` -build.cmd/sh -subset mono+libs.tests -test +build.cmd/sh -subset mono+libs+libs.tests -test -arch x86 ``` -- The following builds and runs all tests in release configuration: +## Partial Build and Test Runs + +Doing full build and test runs takes a long time and is very inefficient if you need to iterate on a change. +For greater control and efficiency individual parts of the build + testing workflow can be run in isolation. +See the [Building instructions](../../building/libraries/README.md) for more info on build options. + +### Test Run Pre-requisites +Before any tests can run we need a complete build to run them on. This requires building (1) a runtime, and +(2) all the libraries. Examples: + +- Build release clr + debug libraries ``` -build.cmd/sh -subset libs.tests -test -c Release +build.cmd/sh -subset clr+libs -rc Release ``` -- The following builds clr in release, libs in debug and runs all tests: +- Build release mono + debug libraries ``` -build.cmd/sh -subset clr+libs+libs.tests -test -rc Release +build.cmd/sh -subset mono+libs -rc Release ``` -- The following builds mono and libs for x86 architecture and runs all tests: +Building the `libs` subset or any of individual library projects automatically copies product binaries into the testhost folder +in the bin directory. This is where the tests will load the binaries from during the run. However System.Private.CorLib is an +exception - the build does not automatically copy it to the testhost folder. If you [rebuild System.Private.CoreLib](https://github.com/dotnet/runtime/blob/main/docs/workflow/building/libraries/README.md#iterating-on-systemprivatecorelib-changes) you must also build the `libs.pretest` subset to ensure S.P.C is copied before running tests. + +### Running tests for all libraries + +- Build and run all tests in release configuration. ``` -build.cmd/sh -subset mono+libs+libs.tests -test -arch x86 +build.cmd/sh -subset libs.tests -test -c Release ``` -- The following example shows how to pass extra msbuild properties to ignore tests ignored in CI: +- Build the tests without running them ``` -build.cmd/sh -subset libs.tests -test /p:WithoutCategories=IgnoreForCI +build.cmd/sh -subset libs.tests ``` -Unless you specifiy `-testnobuild`, test assemblies are implicitly built when invoking the `Test` action. -- The following shows how to only test the libraries without building them +- Run the tests without building them ``` build.cmd/sh -subset libs.tests -test -testnobuild ``` -## Running tests on the command line +- The following example shows how to pass extra msbuild properties to ignore tests ignored in CI. +``` +build.cmd/sh -subset libs.tests -test /p:WithoutCategories=IgnoreForCI +``` -To build tests you need to specify the `test` subset when invoking build.cmd/sh: `build.cmd/sh -subset libs.tests`. +### Running tests for a single library The easiest (and recommended) way to build and run the tests for a specific library, is to invoke the `Test` target on that library: ```cmd @@ -68,21 +85,21 @@ dotnet build /t:Test /p:TargetArchitecture=x86 There may be multiple projects in some directories so you may need to specify the path to a specific test project to get it to build and run the tests. -#### Running a single test on the command line +### Running a single test on the command line To quickly run or debug a single test from the command line, set the XunitMethodName property, e.g.: ```cmd dotnet build /t:Test /p:XunitMethodName={FullyQualifiedNamespace}.{ClassName}.{MethodName} ``` -#### Running outer loop tests +### Running outer loop tests To run all tests, including "outer loop" tests (which are typically slower and in some test suites less reliable, but which are more comprehensive): ```cmd dotnet build /t:Test /p:Outerloop=true ``` -#### Running tests on a different target framework +### Running tests on a different target framework Each test project can potentially have multiple target frameworks. There are some tests that might be OS-specific, or might be testing an API that is available only on some target frameworks, so the `TargetFrameworks` property specifies the valid target frameworks. By default we will build and run only the default build target framework which is `net5.0`. The rest of the `TargetFrameworks` will need to be built and ran by specifying the `BuildTargetFramework` option, e.g.: ```cmd From 97de5c5aff67892ae66fe65d1da971affe59bd76 Mon Sep 17 00:00:00 2001 From: Mateo Torres-Ruiz Date: Wed, 23 Jun 2021 00:19:10 -0700 Subject: [PATCH 078/107] Add support for multi-arch install locations (#53763) * Add support for multiple architectures inside install_locations * Add install_location tests * Fallback to DOTNET_ROOT on win32 --- .../HostActivation.Tests/CommandExtensions.cs | 5 +- .../InstallLocationCommandResultExtensions.cs | 58 ++++++ .../MultiArchInstallLocation.cs | 184 ++++++++++++++++++ .../MultilevelSDKLookup.cs | 2 +- .../NativeHosting/Nethost.cs | 121 ++++++++++-- .../PortableAppActivation.cs | 14 +- .../RegisteredInstallLocationOverride.cs | 16 +- .../StandaloneAppActivation.cs | 3 +- .../tests/TestUtils/TestProjectFixture.cs | 4 +- src/native/corehost/deps_format.cpp | 2 +- src/native/corehost/fxr/command_line.cpp | 2 +- src/native/corehost/fxr_resolver.cpp | 10 +- src/native/corehost/hostmisc/pal.h | 55 +++--- src/native/corehost/hostmisc/pal.unix.cpp | 169 ++++++++++------ src/native/corehost/hostmisc/pal.windows.cpp | 11 +- src/native/corehost/hostmisc/utils.cpp | 40 +++- src/native/corehost/hostmisc/utils.h | 11 +- .../corehost/test/nativehost/nativehost.cpp | 4 +- 18 files changed, 560 insertions(+), 151 deletions(-) create mode 100644 src/installer/tests/HostActivation.Tests/InstallLocationCommandResultExtensions.cs create mode 100644 src/installer/tests/HostActivation.Tests/MultiArchInstallLocation.cs diff --git a/src/installer/tests/HostActivation.Tests/CommandExtensions.cs b/src/installer/tests/HostActivation.Tests/CommandExtensions.cs index 054e6c39d1436..d8460c1368bdb 100644 --- a/src/installer/tests/HostActivation.Tests/CommandExtensions.cs +++ b/src/installer/tests/HostActivation.Tests/CommandExtensions.cs @@ -35,8 +35,11 @@ public static Command EnableTracingAndCaptureOutputs(this Command command) .CaptureStdErr(); } - public static Command DotNetRoot(this Command command, string dotNetRoot) + public static Command DotNetRoot(this Command command, string dotNetRoot, string architecture = null) { + if (!string.IsNullOrEmpty(architecture)) + return command.EnvironmentVariable($"DOTNET_ROOT_{architecture.ToUpper()}", dotNetRoot); + return command .EnvironmentVariable("DOTNET_ROOT", dotNetRoot) .EnvironmentVariable("DOTNET_ROOT(x86)", dotNetRoot); diff --git a/src/installer/tests/HostActivation.Tests/InstallLocationCommandResultExtensions.cs b/src/installer/tests/HostActivation.Tests/InstallLocationCommandResultExtensions.cs new file mode 100644 index 0000000000000..c85b7d3e56cc5 --- /dev/null +++ b/src/installer/tests/HostActivation.Tests/InstallLocationCommandResultExtensions.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using System.Runtime.InteropServices; +using FluentAssertions; +using Microsoft.DotNet.Cli.Build.Framework; +using Microsoft.DotNet.CoreSetup.Test; +using Xunit; + +namespace HostActivation.Tests +{ + internal static class InstallLocationCommandResultExtensions + { + private static bool IsRunningInWoW64(string rid) => OperatingSystem.IsWindows() && Environment.Is64BitOperatingSystem && rid.Equals("win-x86"); + + public static AndConstraint HaveUsedDotNetRootInstallLocation(this CommandResultAssertions assertion, string installLocation, string rid) + { + return assertion.HaveUsedDotNetRootInstallLocation(installLocation, rid, null); + } + + public static AndConstraint HaveUsedDotNetRootInstallLocation(this CommandResultAssertions assertion, + string installLocation, + string rid, + string arch) + { + // If no arch is passed and we are on Windows, we need the used RID for determining whether or not we are running on WoW64. + if (string.IsNullOrEmpty(arch)) + Assert.NotNull(rid); + + string expectedEnvironmentVariable = !string.IsNullOrEmpty(arch) ? $"DOTNET_ROOT_{arch.ToUpper()}" : + IsRunningInWoW64(rid) ? "DOTNET_ROOT(x86)" : "DOTNET_ROOT"; + + return assertion.HaveStdErrContaining($"Using environment variable {expectedEnvironmentVariable}=[{installLocation}] as runtime location."); + } + + public static AndConstraint HaveUsedConfigFileInstallLocation(this CommandResultAssertions assertion, string installLocation) + { + return assertion.HaveStdErrContaining($"Using install location '{installLocation}'."); + } + + public static AndConstraint HaveUsedGlobalInstallLocation(this CommandResultAssertions assertion, string installLocation) + { + return assertion.HaveStdErrContaining($"Using global installation location [{installLocation}]"); + } + + public static AndConstraint HaveFoundDefaultInstallLocationInConfigFile(this CommandResultAssertions assertion, string installLocation) + { + return assertion.HaveStdErrContaining($"Found install location path '{installLocation}'."); + } + + public static AndConstraint HaveFoundArchSpecificInstallLocationInConfigFile(this CommandResultAssertions assertion, string installLocation, string arch) + { + return assertion.HaveStdErrContaining($"Found architecture-specific install location path: '{installLocation}' ('{arch}')."); + } + } +} diff --git a/src/installer/tests/HostActivation.Tests/MultiArchInstallLocation.cs b/src/installer/tests/HostActivation.Tests/MultiArchInstallLocation.cs new file mode 100644 index 0000000000000..c1b00cafd4e6a --- /dev/null +++ b/src/installer/tests/HostActivation.Tests/MultiArchInstallLocation.cs @@ -0,0 +1,184 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Runtime.InteropServices; +using Microsoft.DotNet.Cli.Build.Framework; +using Microsoft.DotNet.CoreSetup.Test; +using Microsoft.DotNet.CoreSetup.Test.HostActivation; +using Xunit; + +namespace HostActivation.Tests +{ + public class MultiArchInstallLocation : IClassFixture + { + private SharedTestState sharedTestState; + + public MultiArchInstallLocation(SharedTestState fixture) + { + sharedTestState = fixture; + } + + [Fact] + public void EnvironmentVariable_CurrentArchitectureIsUsedIfEnvVarSet() + { + var fixture = sharedTestState.PortableAppFixture + .Copy(); + + var appExe = fixture.TestProject.AppExe; + var arch = fixture.RepoDirProvider.BuildArchitecture.ToUpper(); + Command.Create(appExe) + .EnableTracingAndCaptureOutputs() + .DotNetRoot(fixture.BuiltDotnet.BinPath, arch) + .Execute() + .Should().Pass() + .And.HaveUsedDotNetRootInstallLocation(fixture.BuiltDotnet.BinPath, fixture.CurrentRid, arch); + } + + [Fact] + public void EnvironmentVariable_IfNoArchSpecificEnvVarIsFoundDotnetRootIsUsed() + { + var fixture = sharedTestState.PortableAppFixture + .Copy(); + + var appExe = fixture.TestProject.AppExe; + var arch = fixture.RepoDirProvider.BuildArchitecture.ToUpper(); + Command.Create(appExe) + .EnableTracingAndCaptureOutputs() + .DotNetRoot(fixture.BuiltDotnet.BinPath) + .Execute() + .Should().Pass() + .And.HaveUsedDotNetRootInstallLocation(fixture.BuiltDotnet.BinPath, fixture.CurrentRid); + } + + [Fact] + public void EnvironmentVariable_ArchSpecificDotnetRootIsUsedOverDotnetRoot() + { + var fixture = sharedTestState.PortableAppFixture + .Copy(); + + var appExe = fixture.TestProject.AppExe; + var arch = fixture.RepoDirProvider.BuildArchitecture.ToUpper(); + var dotnet = fixture.BuiltDotnet.BinPath; + Command.Create(appExe) + .EnableTracingAndCaptureOutputs() + .DotNetRoot("non_existent_path") + .DotNetRoot(dotnet, arch) + .Execute() + .Should().Pass() + .And.HaveUsedDotNetRootInstallLocation(dotnet, fixture.CurrentRid, arch) + .And.NotHaveStdErrContaining("Using environment variable DOTNET_ROOT="); + } + + [Fact] + [SkipOnPlatform(TestPlatforms.Windows, "This test targets the install_location config file which is only used on Linux and macOS.")] + public void InstallLocationFile_ArchSpecificLocationIsPickedFirst() + { + var fixture = sharedTestState.PortableAppFixture + .Copy(); + + var appExe = fixture.TestProject.AppExe; + var arch1 = "someArch"; + var path1 = "x/y/z"; + var arch2 = fixture.RepoDirProvider.BuildArchitecture; + var path2 = "a/b/c"; + + using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride(appExe)) + { + registeredInstallLocationOverride.SetInstallLocation(new (string, string)[] { + (string.Empty, path1), + (arch1, path1), + (arch2, path2) + }); + + Command.Create(appExe) + .EnableTracingAndCaptureOutputs() + .ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride) + .DotNetRoot(null) + .Execute() + .Should().HaveFoundDefaultInstallLocationInConfigFile(path1) + .And.HaveFoundArchSpecificInstallLocationInConfigFile(path1, arch1) + .And.HaveFoundArchSpecificInstallLocationInConfigFile(path2, arch2) + .And.HaveUsedGlobalInstallLocation(path2); + } + } + + [Fact] + [SkipOnPlatform(TestPlatforms.Windows, "This test targets the install_location config file which is only used on Linux and macOS.")] + public void InstallLocationFile_OnlyFirstLineMayNotSpecifyArchitecture() + { + var fixture = sharedTestState.PortableAppFixture + .Copy(); + + var appExe = fixture.TestProject.AppExe; + using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride(appExe)) + { + registeredInstallLocationOverride.SetInstallLocation(new (string, string)[] { + (string.Empty, "a/b/c"), + (string.Empty, "x/y/z"), + }); + Command.Create(appExe) + .EnableTracingAndCaptureOutputs() + .ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride) + .DotNetRoot(null) + .Execute() + .Should().HaveFoundDefaultInstallLocationInConfigFile("a/b/c") + .And.HaveStdErrContaining($"Only the first line in '{registeredInstallLocationOverride.PathValueOverride}' may not have an architecture prefix.") + .And.HaveUsedConfigFileInstallLocation("a/b/c"); + } + } + + [Fact] + [SkipOnPlatform(TestPlatforms.Windows, "This test targets the install_location config file which is only used on Linux and macOS.")] + public void InstallLocationFile_ReallyLongInstallPathIsParsedCorrectly() + { + var fixture = sharedTestState.PortableAppFixture + .Copy(); + + var appExe = fixture.TestProject.AppExe; + using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride(appExe)) + { + var reallyLongPath = + "reallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreally" + + "reallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreally" + + "reallyreallyreallyreallyreallyreallyreallyreallyreallyreallylongpath"; + registeredInstallLocationOverride.SetInstallLocation((string.Empty, reallyLongPath)); + + Command.Create(appExe) + .EnableTracingAndCaptureOutputs() + .ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride) + .DotNetRoot(null) + .Execute() + .Should().HaveFoundDefaultInstallLocationInConfigFile(reallyLongPath) + .And.HaveUsedConfigFileInstallLocation(reallyLongPath); + } + } + + public class SharedTestState : IDisposable + { + public string BaseDirectory { get; } + public TestProjectFixture PortableAppFixture { get; } + public RepoDirectoriesProvider RepoDirectories { get; } + public string InstallLocation { get; } + + public SharedTestState() + { + RepoDirectories = new RepoDirectoriesProvider(); + var fixture = new TestProjectFixture("PortableApp", RepoDirectories); + fixture + .EnsureRestored() + // App Host generation is turned off by default on macOS + .PublishProject(extraArgs: "/p:UseAppHost=true"); + + PortableAppFixture = fixture; + BaseDirectory = Path.GetDirectoryName(PortableAppFixture.SdkDotnet.GreatestVersionHostFxrFilePath); + } + + public void Dispose() + { + PortableAppFixture.Dispose(); + } + } + } +} diff --git a/src/installer/tests/HostActivation.Tests/MultilevelSDKLookup.cs b/src/installer/tests/HostActivation.Tests/MultilevelSDKLookup.cs index 97b3387604b62..76d11337c03ab 100644 --- a/src/installer/tests/HostActivation.Tests/MultilevelSDKLookup.cs +++ b/src/installer/tests/HostActivation.Tests/MultilevelSDKLookup.cs @@ -501,7 +501,7 @@ public void SdkMultilevelLookup_RegistryAccess() using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride(DotNet.GreatestVersionHostFxrFilePath)) { - registeredInstallLocationOverride.SetInstallLocation(_regDir, RepoDirectories.BuildArchitecture); + registeredInstallLocationOverride.SetInstallLocation(new (string, string)[] { (RepoDirectories.BuildArchitecture, _regDir) }); // Add SDK versions AddAvailableSdkVersions(_regSdkBaseDir, "9999.0.4"); diff --git a/src/installer/tests/HostActivation.Tests/NativeHosting/Nethost.cs b/src/installer/tests/HostActivation.Tests/NativeHosting/Nethost.cs index 0f18161b02682..0af2ce3477412 100644 --- a/src/installer/tests/HostActivation.Tests/NativeHosting/Nethost.cs +++ b/src/installer/tests/HostActivation.Tests/NativeHosting/Nethost.cs @@ -116,7 +116,7 @@ public void GetHostFxrPath_GlobalInstallation(bool explicitLoad, bool useAssembl { if (useRegisteredLocation) { - registeredInstallLocationOverride.SetInstallLocation(installLocation, sharedState.RepoDirectories.BuildArchitecture); + registeredInstallLocationOverride.SetInstallLocation((sharedState.RepoDirectories.BuildArchitecture, installLocation)); } result = Command.Create(sharedState.NativeHostPath, $"{GetHostFxrPath} {explicitLoad} {(useAssemblyPath ? sharedState.TestAssemblyPath : string.Empty)}") @@ -180,21 +180,32 @@ public void GetHostFxrPath_HostFxrAlreadyLoaded() [Theory] [SkipOnPlatform(TestPlatforms.Windows, "This test targets the install_location config file which is only used on Linux and macOS.")] - [InlineData("{0}", true)] - [InlineData("{0}\n", true)] - [InlineData("{0}\nSome other text", true)] - [InlineData("", false)] - [InlineData("\n{0}", false)] - [InlineData(" {0}", false)] - [InlineData("{0} \n", false)] - [InlineData("{0} ", false)] - public void GetHostFxrPath_InstallLocationFile(string value, bool shouldPass) + [InlineData("{0}", false, true)] + [InlineData("{0}\n", false, true)] + [InlineData("{0}\nSome other text", false, true)] + [InlineData("", false, false)] + [InlineData("\n{0}", false, false)] + [InlineData(" {0}", false, false)] + [InlineData("{0} \n", false, false)] + [InlineData("{0} ", false, false)] + [InlineData("{0}", true, true)] + [InlineData("{0}\n", true, true)] + [InlineData("{0}\nSome other text", true, true)] + [InlineData("", true, false)] + [InlineData("\n{0}", true, false)] + [InlineData(" {0}", true, false)] + [InlineData("{0} \n", true, false)] + [InlineData("{0} ", true, false)] + public void GetHostFxrPath_InstallLocationFile(string value, bool shouldUseArchSpecificInstallLocation, bool shouldPass) { string installLocation = Path.Combine(sharedState.ValidInstallRoot, "dotnet"); using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride(sharedState.NethostPath)) { - File.WriteAllText(registeredInstallLocationOverride.PathValueOverride, string.Format(value, installLocation)); + if (shouldUseArchSpecificInstallLocation) + registeredInstallLocationOverride.SetInstallLocation((sharedState.RepoDirectories.BuildArchitecture, string.Format(value, installLocation))); + else + registeredInstallLocationOverride.SetInstallLocation((string.Empty, string.Format(value, installLocation))); CommandResult result = Command.Create(sharedState.NativeHostPath, GetHostFxrPath) .EnableTracingAndCaptureOutputs() @@ -223,6 +234,94 @@ public void GetHostFxrPath_InstallLocationFile(string value, bool shouldPass) } } + [Fact] + [SkipOnPlatform(TestPlatforms.Windows, "This test targets the install_location config file which is only used on Linux and macOS.")] + public void GetHostFxrPath_GlobalInstallation_HasMoreThanOneDefaultInstallationPath() + { + string installLocation = Path.Combine(sharedState.ValidInstallRoot, "dotnet"); + using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride(sharedState.NethostPath)) + { + registeredInstallLocationOverride.SetInstallLocation(new (string, string)[] { + (string.Empty, installLocation), (string.Empty, installLocation) + }); + + CommandResult result = Command.Create(sharedState.NativeHostPath, GetHostFxrPath) + .EnableTracingAndCaptureOutputs() + .ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride) + .EnvironmentVariable( // Redirect the default install location to an invalid location so that it doesn't cause the test to pass + Constants.TestOnlyEnvironmentVariables.DefaultInstallPath, + sharedState.InvalidInstallRoot) + .DotNetRoot(null) + .Execute(); + + result.Should().Pass() + .And.HaveStdErrContaining($"Looking for install_location file in '{registeredInstallLocationOverride.PathValueOverride}'.") + .And.HaveStdErrContaining($"Found install location path '{installLocation}'.") + .And.HaveStdErrContaining($"Only the first line in '{registeredInstallLocationOverride.PathValueOverride}' may not have an architecture prefix.") + .And.HaveStdErrContaining($"Using install location '{installLocation}'.") + .And.HaveStdOutContaining($"hostfxr_path: {sharedState.HostFxrPath}".ToLower()); + } + } + + [Fact] + [SkipOnPlatform(TestPlatforms.Windows, "This test targets the install_location config file which is only used on Linux and macOS.")] + public void GetHostFxrPath_GlobalInstallation_HasNoDefaultInstallationPath() + { + string installLocation = Path.Combine(sharedState.ValidInstallRoot, "dotnet"); + using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride(sharedState.NethostPath)) + { + registeredInstallLocationOverride.SetInstallLocation(new (string, string)[] { + (sharedState.RepoDirectories.BuildArchitecture, installLocation), + ("someOtherArch", $"{installLocation}/invalid") + }); + + CommandResult result = Command.Create(sharedState.NativeHostPath, GetHostFxrPath) + .EnableTracingAndCaptureOutputs() + .ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride) + .EnvironmentVariable( // Redirect the default install location to an invalid location so that it doesn't cause the test to pass + Constants.TestOnlyEnvironmentVariables.DefaultInstallPath, + sharedState.InvalidInstallRoot) + .DotNetRoot(null) + .Execute(); + + result.Should().Pass() + .And.HaveStdErrContaining($"Looking for install_location file in '{registeredInstallLocationOverride.PathValueOverride}'.") + .And.HaveStdErrContaining($"Found architecture-specific install location path: '{installLocation}' ('{sharedState.RepoDirectories.BuildArchitecture.ToLower()}').") + .And.HaveStdErrContaining($"Using install location '{installLocation}'.") + .And.HaveStdOutContaining($"hostfxr_path: {sharedState.HostFxrPath}".ToLower()); + } + } + + [Fact] + [SkipOnPlatform(TestPlatforms.Windows, "This test targets the install_location config file which is only used on Linux and macOS.")] + public void GetHostFxrPath_GlobalInstallation_ArchitectureSpecificPathIsPickedOverDefaultPath() + { + string installLocation = Path.Combine(sharedState.ValidInstallRoot, "dotnet"); + using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride(sharedState.NethostPath)) + { + registeredInstallLocationOverride.SetInstallLocation(new (string, string)[] { + (string.Empty, $"{installLocation}/a/b/c"), + (sharedState.RepoDirectories.BuildArchitecture, installLocation) + }); + + CommandResult result = Command.Create(sharedState.NativeHostPath, GetHostFxrPath) + .EnableTracingAndCaptureOutputs() + .ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride) + .EnvironmentVariable( // Redirect the default install location to an invalid location so that it doesn't cause the test to pass + Constants.TestOnlyEnvironmentVariables.DefaultInstallPath, + sharedState.InvalidInstallRoot) + .DotNetRoot(null) + .Execute(); + + result.Should().Pass() + .And.HaveStdErrContaining($"Looking for install_location file in '{registeredInstallLocationOverride.PathValueOverride}'.") + .And.HaveStdErrContaining($"Found install location path '{installLocation}/a/b/c'.") + .And.HaveStdErrContaining($"Found architecture-specific install location path: '{installLocation}' ('{sharedState.RepoDirectories.BuildArchitecture.ToLower()}').") + .And.HaveStdErrContaining($"Using install location '{installLocation}'.") + .And.HaveStdOutContaining($"hostfxr_path: {sharedState.HostFxrPath}".ToLower()); + } + } + [Fact] public void GetHostFxrPath_InvalidParameters() { diff --git a/src/installer/tests/HostActivation.Tests/PortableAppActivation.cs b/src/installer/tests/HostActivation.Tests/PortableAppActivation.cs index 14060b5e944bf..a2a0d963e1f09 100644 --- a/src/installer/tests/HostActivation.Tests/PortableAppActivation.cs +++ b/src/installer/tests/HostActivation.Tests/PortableAppActivation.cs @@ -257,7 +257,7 @@ public void AppHost_FrameworkDependent_Succeeds() Command.Create(appExe) .CaptureStdErr() .CaptureStdOut() - .DotNetRoot(builtDotnet) + .DotNetRoot(builtDotnet, sharedTestState.RepoDirectories.BuildArchitecture) .MultilevelLookup(false) .Execute() .Should().Pass() @@ -268,7 +268,7 @@ public void AppHost_FrameworkDependent_Succeeds() // Verify running from within the working directory Command.Create(appExe) .WorkingDirectory(fixture.TestProject.OutputDirectory) - .DotNetRoot(builtDotnet) + .DotNetRoot(builtDotnet, sharedTestState.RepoDirectories.BuildArchitecture) .MultilevelLookup(false) .CaptureStdErr() .CaptureStdOut() @@ -297,10 +297,10 @@ public void AppHost_FrameworkDependent_GlobalLocation_Succeeds(bool useRegistere using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride(appExe)) { - string architecture = fixture.CurrentRid.Split('-')[1]; + string architecture = fixture.RepoDirProvider.BuildArchitecture; if (useRegisteredLocation) { - registeredInstallLocationOverride.SetInstallLocation(builtDotnet, architecture); + registeredInstallLocationOverride.SetInstallLocation(new (string, string)[] { (architecture, builtDotnet) }); } // Verify running with the default working directory @@ -357,7 +357,7 @@ public void MissingRuntimeConfig_Fails(bool useAppHost) if (useAppHost) { command = Command.Create(sharedTestState.MockApp.AppExe) - .DotNetRoot(sharedTestState.BuiltDotNet.BinPath); + .DotNetRoot(sharedTestState.BuiltDotNet.BinPath, sharedTestState.RepoDirectories.BuildArchitecture); } else { @@ -386,7 +386,7 @@ public void MissingFrameworkInRuntimeConfig_Fails(bool useAppHost) if (useAppHost) { command = Command.Create(app.AppExe) - .DotNetRoot(sharedTestState.BuiltDotNet.BinPath); + .DotNetRoot(sharedTestState.BuiltDotNet.BinPath, sharedTestState.RepoDirectories.BuildArchitecture); } else { @@ -540,7 +540,7 @@ public void AppHost_GUI_NoCustomErrorWriter_FrameworkMissing_ErrorReportedInDial Command command = Command.Create(appExe) .EnableTracingAndCaptureOutputs() - .DotNetRoot(dotnet.BinPath) + .DotNetRoot(dotnet.BinPath, sharedTestState.RepoDirectories.BuildArchitecture) .MultilevelLookup(false) .Start(); diff --git a/src/installer/tests/HostActivation.Tests/RegisteredInstallLocationOverride.cs b/src/installer/tests/HostActivation.Tests/RegisteredInstallLocationOverride.cs index 7a30c5d6fd08d..d37cfafe64953 100644 --- a/src/installer/tests/HostActivation.Tests/RegisteredInstallLocationOverride.cs +++ b/src/installer/tests/HostActivation.Tests/RegisteredInstallLocationOverride.cs @@ -4,8 +4,10 @@ using Microsoft.DotNet.Cli.Build.Framework; using Microsoft.Win32; using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; using System.Runtime.InteropServices; namespace Microsoft.DotNet.CoreSetup.Test.HostActivation @@ -61,18 +63,24 @@ public RegisteredInstallLocationOverride(string productBinaryPath) } } - public void SetInstallLocation(string installLocation, string architecture) + public void SetInstallLocation(params (string Architecture, string Path)[] locations) { + Debug.Assert(locations.Length >= 1); if (OperatingSystem.IsWindows()) { - using (RegistryKey dotnetLocationKey = key.CreateSubKey($@"Setup\InstalledVersions\{architecture}")) + foreach (var location in locations) { - dotnetLocationKey.SetValue("InstallLocation", installLocation); + using (RegistryKey dotnetLocationKey = key.CreateSubKey($@"Setup\InstalledVersions\{location.Architecture}")) + { + dotnetLocationKey.SetValue("InstallLocation", location.Path); + } } } else { - File.WriteAllText(PathValueOverride, installLocation); + File.WriteAllText(PathValueOverride, string.Join(Environment.NewLine, + locations.Select(l => string.Format("{0}{1}", + (!string.IsNullOrWhiteSpace(l.Architecture) ? l.Architecture + "=" : ""), l.Path)))); } } diff --git a/src/installer/tests/HostActivation.Tests/StandaloneAppActivation.cs b/src/installer/tests/HostActivation.Tests/StandaloneAppActivation.cs index b7ec6eb968891..352abb04f0dc1 100644 --- a/src/installer/tests/HostActivation.Tests/StandaloneAppActivation.cs +++ b/src/installer/tests/HostActivation.Tests/StandaloneAppActivation.cs @@ -204,8 +204,7 @@ public void Running_Publish_Output_Standalone_EXE_With_DOTNET_ROOT_Fails() // self-contained layout since a flat layout of the shared framework is not supported. Command.Create(appExe) .EnvironmentVariable("COREHOST_TRACE", "1") - .EnvironmentVariable("DOTNET_ROOT", newOutDir) - .EnvironmentVariable("DOTNET_ROOT(x86)", newOutDir) + .DotNetRoot(newOutDir) .CaptureStdErr() .CaptureStdOut() .Execute(fExpectedToFail: true) diff --git a/src/installer/tests/TestUtils/TestProjectFixture.cs b/src/installer/tests/TestUtils/TestProjectFixture.cs index 2ccce4dded155..7b9da29d1a908 100644 --- a/src/installer/tests/TestUtils/TestProjectFixture.cs +++ b/src/installer/tests/TestUtils/TestProjectFixture.cs @@ -358,7 +358,7 @@ public TestProjectFixture RestoreProject(string[] fallbackSources, string extraM public TestProjectFixture EnsureRestored(params string[] fallbackSources) { - if ( ! TestProject.IsRestored()) + if (!TestProject.IsRestored()) { RestoreProject(fallbackSources); } @@ -368,7 +368,7 @@ public TestProjectFixture EnsureRestored(params string[] fallbackSources) public TestProjectFixture EnsureRestoredForRid(string rid, params string[] fallbackSources) { - if ( ! TestProject.IsRestored()) + if (!TestProject.IsRestored()) { string extraMSBuildProperties = $"/p:TestTargetRid={rid}"; RestoreProject(fallbackSources, extraMSBuildProperties); diff --git a/src/native/corehost/deps_format.cpp b/src/native/corehost/deps_format.cpp index f0beb6ed7e54e..8ab0f22caff2c 100644 --- a/src/native/corehost/deps_format.cpp +++ b/src/native/corehost/deps_format.cpp @@ -84,7 +84,7 @@ void deps_json_t::reconcile_libraries_with_targets( size_t pos = lib_name.find(_X("/")); entry.library_name = lib_name.substr(0, pos); entry.library_version = lib_name.substr(pos + 1); - entry.library_type = pal::to_lower(library.value[_X("type")].GetString()); + entry.library_type = to_lower(library.value[_X("type")].GetString()); entry.library_hash = hash; entry.library_path = library_path; entry.library_hash_path = library_hash_path; diff --git a/src/native/corehost/fxr/command_line.cpp b/src/native/corehost/fxr/command_line.cpp index 082f81d802c12..deb80b38b7f52 100644 --- a/src/native/corehost/fxr/command_line.cpp +++ b/src/native/corehost/fxr/command_line.cpp @@ -86,7 +86,7 @@ namespace while (arg_i < argc) { const pal::char_t* arg = argv[arg_i]; - pal::string_t arg_lower = pal::to_lower(arg); + pal::string_t arg_lower = to_lower(arg); const auto &iter = std::find_if(known_opts.cbegin(), known_opts.cend(), [&](const known_options &opt) { return arg_lower == get_host_option(opt).option; }); if (iter == known_opts.cend()) diff --git a/src/native/corehost/fxr_resolver.cpp b/src/native/corehost/fxr_resolver.cpp index d457047ddc8f9..6e1356457d49a 100644 --- a/src/native/corehost/fxr_resolver.cpp +++ b/src/native/corehost/fxr_resolver.cpp @@ -65,10 +65,10 @@ bool fxr_resolver::try_get_path(const pal::string_t& root_path, pal::string_t* o return true; } - // For framework-dependent apps, use DOTNET_ROOT + // For framework-dependent apps, use DOTNET_ROOT_ pal::string_t default_install_location; - pal::string_t dotnet_root_env_var_name = get_dotnet_root_env_var_name(); - if (get_file_path_from_env(dotnet_root_env_var_name.c_str(), out_dotnet_root)) + pal::string_t dotnet_root_env_var_name; + if (get_dotnet_root_from_env(&dotnet_root_env_var_name, out_dotnet_root)) { trace::info(_X("Using environment variable %s=[%s] as runtime location."), dotnet_root_env_var_name.c_str(), out_dotnet_root->c_str()); } @@ -134,7 +134,7 @@ bool fxr_resolver::try_get_path(const pal::string_t& root_path, pal::string_t* o #endif // !FEATURE_APPHOST && !FEATURE_LIBHOST } -bool fxr_resolver::try_get_path_from_dotnet_root(const pal::string_t &dotnet_root, pal::string_t *out_fxr_path) +bool fxr_resolver::try_get_path_from_dotnet_root(const pal::string_t& dotnet_root, pal::string_t* out_fxr_path) { pal::string_t fxr_dir = dotnet_root; append_path(&fxr_dir, _X("host")); @@ -148,7 +148,7 @@ bool fxr_resolver::try_get_path_from_dotnet_root(const pal::string_t &dotnet_roo return get_latest_fxr(std::move(fxr_dir), out_fxr_path); } -bool fxr_resolver::try_get_existing_fxr(pal::dll_t *out_fxr, pal::string_t *out_fxr_path) +bool fxr_resolver::try_get_existing_fxr(pal::dll_t* out_fxr, pal::string_t* out_fxr_path) { if (!pal::get_loaded_library(LIBFXR_NAME, "hostfxr_main", out_fxr, out_fxr_path)) return false; diff --git a/src/native/corehost/hostmisc/pal.h b/src/native/corehost/hostmisc/pal.h index 27daf76c72723..b6622cd5dff52 100644 --- a/src/native/corehost/hostmisc/pal.h +++ b/src/native/corehost/hostmisc/pal.h @@ -104,13 +104,13 @@ namespace pal { #if defined(_WIN32) - #ifdef EXPORT_SHARED_API - #define SHARED_API extern "C" __declspec(dllexport) - #else - #define SHARED_API extern "C" - #endif +#ifdef EXPORT_SHARED_API +#define SHARED_API extern "C" __declspec(dllexport) +#else +#define SHARED_API extern "C" +#endif - #define STDMETHODCALLTYPE __stdcall +#define STDMETHODCALLTYPE __stdcall typedef wchar_t char_t; typedef std::wstring string_t; @@ -151,13 +151,13 @@ namespace pal inline int strcasecmp(const char_t* str1, const char_t* str2) { return ::_wcsicmp(str1, str2); } inline int strncmp(const char_t* str1, const char_t* str2, size_t len) { return ::wcsncmp(str1, str2, len); } inline int strncasecmp(const char_t* str1, const char_t* str2, size_t len) { return ::_wcsnicmp(str1, str2, len); } - inline int pathcmp(const pal::string_t &path1, const pal::string_t &path2) { return strcasecmp(path1.c_str(), path2.c_str()); } + inline int pathcmp(const pal::string_t& path1, const pal::string_t& path2) { return strcasecmp(path1.c_str(), path2.c_str()); } inline string_t to_string(int value) { return std::to_wstring(value); } inline size_t strlen(const char_t* str) { return ::wcslen(str); } #pragma warning(suppress : 4996) // error C4996: '_wfopen': This function or variable may be unsafe. - inline FILE * file_open(const string_t& path, const char_t* mode) { return ::_wfopen(path.c_str(), mode); } + inline FILE* file_open(const string_t& path, const char_t* mode) { return ::_wfopen(path.c_str(), mode); } inline void file_vprintf(FILE* f, const char_t* format, va_list vl) { ::vfwprintf(f, format, vl); ::fputwc(_X('\n'), f); } inline void err_fputs(const char_t* message) { ::fputws(message, stderr); ::fputwc(_X('\n'), stderr); } @@ -170,32 +170,32 @@ namespace pal // Suppressing warning since the 'safe' version requires an input buffer that is unnecessary for // uses of this function. #pragma warning(suppress : 4996) // error C4996: '_wcserror': This function or variable may be unsafe. - inline const char_t* strerror(int errnum){ return ::_wcserror(errnum); } + inline const char_t* strerror(int errnum) { return ::_wcserror(errnum); } bool pal_utf8string(const string_t& str, std::vector* out); bool pal_clrstring(const string_t& str, std::vector* out); bool clr_palstring(const char* cstr, string_t* out); inline bool mkdir(const char_t* dir, int mode) { return CreateDirectoryW(dir, NULL) != 0; } - inline bool rmdir (const char_t* path) { return RemoveDirectoryW(path) != 0; } + inline bool rmdir(const char_t* path) { return RemoveDirectoryW(path) != 0; } inline int rename(const char_t* old_name, const char_t* new_name) { return ::_wrename(old_name, new_name); } inline int remove(const char_t* path) { return ::_wremove(path); } inline bool munmap(void* addr, size_t length) { return UnmapViewOfFile(addr) != 0; } inline int get_pid() { return GetCurrentProcessId(); } inline void sleep(uint32_t milliseconds) { Sleep(milliseconds); } #else - #ifdef EXPORT_SHARED_API - #define SHARED_API extern "C" __attribute__((__visibility__("default"))) - #else - #define SHARED_API extern "C" - #endif - - #define __cdecl /* nothing */ - #define __stdcall /* nothing */ - #if !defined(TARGET_FREEBSD) - #define __fastcall /* nothing */ - #endif - #define STDMETHODCALLTYPE __stdcall +#ifdef EXPORT_SHARED_API +#define SHARED_API extern "C" __attribute__((__visibility__("default"))) +#else +#define SHARED_API extern "C" +#endif + +#define __cdecl /* nothing */ +#define __stdcall /* nothing */ +#if !defined(TARGET_FREEBSD) +#define __fastcall /* nothing */ +#endif +#define STDMETHODCALLTYPE __stdcall typedef char char_t; typedef std::string string_t; @@ -219,7 +219,7 @@ namespace pal inline string_t to_string(int value) { return std::to_string(value); } inline size_t strlen(const char_t* str) { return ::strlen(str); } - inline FILE * file_open(const string_t& path, const char_t* mode) { return fopen(path.c_str(), mode); } + inline FILE* file_open(const string_t& path, const char_t* mode) { return fopen(path.c_str(), mode); } inline void file_vprintf(FILE* f, const char_t* format, va_list vl) { ::vfprintf(f, format, vl); ::fputc('\n', f); } inline void err_fputs(const char_t* message) { ::fputs(message, stderr); ::fputc(_X('\n'), stderr); } inline void out_vprintf(const char_t* format, va_list vl) { ::vfprintf(stdout, format, vl); ::fputc('\n', stdout); } @@ -237,7 +237,6 @@ namespace pal inline bool munmap(void* addr, size_t length) { return ::munmap(addr, length) == 0; } inline int get_pid() { return getpid(); } inline void sleep(uint32_t milliseconds) { usleep(milliseconds * 1000); } - #endif inline int snwprintf(char_t* buffer, size_t count, const char_t* format, ...) @@ -252,10 +251,8 @@ namespace pal string_t get_timestamp(); bool getcwd(string_t* recv); - string_t to_lower(const char_t* in); - - inline void file_flush(FILE *f) { std::fflush(f); } + inline void file_flush(FILE* f) { std::fflush(f); } inline void err_flush() { std::fflush(stderr); } inline void out_flush() { std::fflush(stdout); } @@ -283,7 +280,7 @@ namespace pal bool get_own_module_path(string_t* recv); bool get_method_module_path(string_t* recv, void* method); bool get_module_path(dll_t mod, string_t* recv); - bool get_current_module(dll_t *mod); + bool get_current_module(dll_t* mod); bool getenv(const char_t* name, string_t* recv); bool get_default_servicing_directory(string_t* recv); @@ -307,7 +304,7 @@ namespace pal int xtoi(const char_t* input); - bool get_loaded_library(const char_t *library_name, const char *symbol_name, /*out*/ dll_t *dll, /*out*/ string_t *path); + bool get_loaded_library(const char_t* library_name, const char* symbol_name, /*out*/ dll_t* dll, /*out*/ string_t* path); bool load_library(const string_t* path, dll_t* dll); proc_t get_symbol(dll_t library, const char* name); void unload_library(dll_t library); diff --git a/src/native/corehost/hostmisc/pal.unix.cpp b/src/native/corehost/hostmisc/pal.unix.cpp index 2db2dd8488f56..411298a08f4ce 100644 --- a/src/native/corehost/hostmisc/pal.unix.cpp +++ b/src/native/corehost/hostmisc/pal.unix.cpp @@ -50,13 +50,6 @@ #error "Don't know how to obtain max path on this platform" #endif -pal::string_t pal::to_lower(const pal::char_t* in) -{ - pal::string_t ret = in; - std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); - return ret; -} - pal::string_t pal::get_timestamp() { std::time_t t = std::time(nullptr); @@ -75,7 +68,7 @@ bool pal::touch_file(const pal::string_t& path) trace::warning(_X("open(%s) failed in %s"), path.c_str(), _STRINGIFY(__FUNCTION__)); return false; } - (void) close(fd); + (void)close(fd); return true; } @@ -146,12 +139,12 @@ bool pal::getcwd(pal::string_t* recv) namespace { - bool get_loaded_library_from_proc_maps(const pal::char_t *library_name, pal::dll_t *dll, pal::string_t *path) + bool get_loaded_library_from_proc_maps(const pal::char_t* library_name, pal::dll_t* dll, pal::string_t* path) { - char *line = nullptr; + char* line = nullptr; size_t lineLen = 0; ssize_t read; - FILE *file = pal::file_open(_X("/proc/self/maps"), _X("r")); + FILE* file = pal::file_open(_X("/proc/self/maps"), _X("r")); if (file == nullptr) return false; @@ -192,10 +185,10 @@ namespace } bool pal::get_loaded_library( - const char_t *library_name, - const char *symbol_name, - /*out*/ dll_t *dll, - /*out*/ pal::string_t *path) + const char_t* library_name, + const char* symbol_name, + /*out*/ dll_t* dll, + /*out*/ pal::string_t* path) { pal::string_t library_name_local; #if defined(TARGET_OSX) @@ -340,7 +333,7 @@ bool pal::get_default_servicing_directory(string_t* recv) bool is_read_write_able_directory(pal::string_t& dir) { return pal::realpath(&dir) && - (access(dir.c_str(), R_OK | W_OK | X_OK) == 0); + (access(dir.c_str(), R_OK | W_OK | X_OK) == 0); } bool get_extraction_base_parent_directory(pal::string_t& directory) @@ -388,18 +381,41 @@ bool pal::get_global_dotnet_dirs(std::vector* recv) bool pal::get_dotnet_self_registered_config_location(pal::string_t* recv) { - *recv = _X("/etc/dotnet/install_location"); + recv->assign(_X("/etc/dotnet/install_location")); // ***Used only for testing*** pal::string_t environment_install_location_override; if (test_only_getenv(_X("_DOTNET_TEST_INSTALL_LOCATION_FILE_PATH"), &environment_install_location_override)) { - *recv = environment_install_location_override; + recv->assign(environment_install_location_override); } return true; } +namespace +{ + bool get_line_from_file(FILE* pFile, pal::string_t& line) + { + line = pal::string_t(); + char buffer[256]; + while (fgets(buffer, sizeof(buffer), pFile)) + { + line += (pal::char_t*)buffer; + size_t len = line.length(); + + // fgets includes the newline character in the string - so remove it. + if (len > 0 && line[len - 1] == '\n') + { + line.pop_back(); + break; + } + } + + return !line.empty(); + } +} + bool pal::get_dotnet_self_registered_dir(pal::string_t* recv) { recv->clear(); @@ -424,35 +440,60 @@ bool pal::get_dotnet_self_registered_dir(pal::string_t* recv) FILE* install_location_file = pal::file_open(install_location_file_path, "r"); if (install_location_file == nullptr) { - trace::verbose(_X("The install_location file failed to open.")); + trace::error(_X("The install_location file ['%s'] failed to open: %s."), install_location_file_path.c_str(), pal::strerror(errno)); return false; } - bool result = false; + pal::string_t install_location; + int current_line = 0; + bool is_first_line = true, install_location_found = false; - char buf[PATH_MAX]; - char* install_location = fgets(buf, sizeof(buf), install_location_file); - if (install_location != nullptr) + while (get_line_from_file(install_location_file, install_location)) { - size_t len = pal::strlen(install_location); + current_line++; + size_t arch_sep = install_location.find(_X('=')); + if (arch_sep == pal::string_t::npos) + { + if (is_first_line) + { + recv->assign(install_location); + install_location_found = true; + trace::verbose(_X("Found install location path '%s'."), install_location.c_str()); + } + else + { + trace::warning(_X("Found unprefixed install location path '%s' on line %d."), install_location.c_str(), current_line); + trace::warning(_X("Only the first line in '%s' may not have an architecture prefix."), install_location_file_path.c_str()); + } + + is_first_line = false; + continue; + } + + pal::string_t arch_prefix = install_location.substr(0, arch_sep); + pal::string_t path_to_location = install_location.substr(arch_sep + 1); - // fgets includes the newline character in the string - so remove it. - if (len > 0 && len < PATH_MAX && install_location[len - 1] == '\n') + trace::verbose(_X("Found architecture-specific install location path: '%s' ('%s')."), path_to_location.c_str(), arch_prefix.c_str()); + if (pal::strcasecmp(arch_prefix.c_str(), get_arch()) == 0) { - install_location[len - 1] = '\0'; + recv->assign(path_to_location); + install_location_found = true; + trace::verbose(_X("Found architecture-specific install location path matching the current host architecture ('%s'): '%s'."), arch_prefix.c_str(), path_to_location.c_str()); + break; } - trace::verbose(_X("Using install location '%s'."), install_location); - *recv = install_location; - result = true; + is_first_line = false; } - else + + fclose(install_location_file); + if (!install_location_found) { - trace::verbose(_X("The install_location file first line could not be read.")); + trace::warning(_X("Did not find any install location in '%s'."), install_location_file_path.c_str()); + return false; } - fclose(install_location_file); - return result; + trace::verbose(_X("Using install location '%s'."), recv->c_str()); + return true; } bool pal::get_default_installation_dir(pal::string_t* recv) @@ -467,17 +508,17 @@ bool pal::get_default_installation_dir(pal::string_t* recv) // *************************** #if defined(TARGET_OSX) - recv->assign(_X("/usr/local/share/dotnet")); + recv->assign(_X("/usr/local/share/dotnet")); #else - recv->assign(_X("/usr/share/dotnet")); + recv->assign(_X("/usr/share/dotnet")); #endif - return true; + return true; } pal::string_t trim_quotes(pal::string_t stringToCleanup) { - pal::char_t quote_array[2] = {'\"', '\''}; - for (size_t index = 0; index < sizeof(quote_array)/sizeof(quote_array[0]); index++) + pal::char_t quote_array[2] = { '\"', '\'' }; + for (size_t index = 0; index < sizeof(quote_array) / sizeof(quote_array[0]); index++) { size_t pos = stringToCleanup.find(quote_array[index]); while (pos != std::string::npos) @@ -553,11 +594,11 @@ pal::string_t pal::get_current_os_rid_platform() if (ret == 0) { - char *pos = strchr(str, '.'); + char* pos = strchr(str, '.'); if (pos) { ridOS.append(_X("freebsd.")) - .append(str, pos - str); + .append(str, pos - str); } } @@ -589,7 +630,7 @@ pal::string_t pal::get_current_os_rid_platform() if (strncmp(utsname_obj.version, "omnios", strlen("omnios")) == 0) { ridOS.append(_X("omnios.")) - .append(utsname_obj.version, strlen("omnios-r"), 2); // e.g. omnios.15 + .append(utsname_obj.version, strlen("omnios-r"), 2); // e.g. omnios.15 } else if (strncmp(utsname_obj.version, "illumos-", strlen("illumos-")) == 0) { @@ -598,7 +639,7 @@ pal::string_t pal::get_current_os_rid_platform() else if (strncmp(utsname_obj.version, "joyent_", strlen("joyent_")) == 0) { ridOS.append(_X("smartos.")) - .append(utsname_obj.version, strlen("joyent_"), 4); // e.g. smartos.2020 + .append(utsname_obj.version, strlen("joyent_"), 4); // e.g. smartos.2020 } return ridOS; @@ -621,11 +662,11 @@ pal::string_t pal::get_current_os_rid_platform() return ridOS; } - char *pos = strchr(utsname_obj.version, '.'); + char* pos = strchr(utsname_obj.version, '.'); if (pos) { ridOS.append(_X("solaris.")) - .append(utsname_obj.version, pos - utsname_obj.version); // e.g. solaris.11 + .append(utsname_obj.version, pos - utsname_obj.version); // e.g. solaris.11 } return ridOS; @@ -771,7 +812,7 @@ bool pal::get_own_executable_path(pal::string_t* recv) bool pal::get_own_module_path(string_t* recv) { Dl_info info; - if (dladdr((void *)&pal::get_own_module_path, &info) == 0) + if (dladdr((void*)&pal::get_own_module_path, &info) == 0) return false; recv->assign(info.dli_fname); @@ -793,7 +834,7 @@ bool pal::get_module_path(dll_t module, string_t* recv) return false; } -bool pal::get_current_module(dll_t *mod) +bool pal::get_current_module(dll_t* mod) { return false; } @@ -876,31 +917,31 @@ static void readdir(const pal::string_t& path, const pal::string_t& pattern, boo } break; - // Handle symlinks and file systems that do not support d_type + // Handle symlinks and file systems that do not support d_type case DT_LNK: case DT_UNKNOWN: - { - struct stat sb; + { + struct stat sb; - if (fstatat(dirfd(dir), entry->d_name, &sb, 0) == -1) - { - continue; - } + if (fstatat(dirfd(dir), entry->d_name, &sb, 0) == -1) + { + continue; + } - if (onlydirectories) - { - if (!S_ISDIR(sb.st_mode)) - { - continue; - } - break; - } - else if (!S_ISREG(sb.st_mode) && !S_ISDIR(sb.st_mode)) + if (onlydirectories) + { + if (!S_ISDIR(sb.st_mode)) { continue; } + break; } - break; + else if (!S_ISREG(sb.st_mode) && !S_ISDIR(sb.st_mode)) + { + continue; + } + } + break; default: continue; diff --git a/src/native/corehost/hostmisc/pal.windows.cpp b/src/native/corehost/hostmisc/pal.windows.cpp index 8a9e35a3236f0..c992f92da8294 100644 --- a/src/native/corehost/hostmisc/pal.windows.cpp +++ b/src/native/corehost/hostmisc/pal.windows.cpp @@ -26,7 +26,7 @@ bool GetModuleFileNameWrapper(HMODULE hModule, pal::string_t* recv) return false; path.resize(dwModuleFileName); - *recv = path; + recv->assign(path); return true; } @@ -40,13 +40,6 @@ bool GetModuleHandleFromAddress(void *addr, HMODULE *hModule) return (res != FALSE); } -pal::string_t pal::to_lower(const pal::char_t* in) -{ - pal::string_t ret = in; - std::transform(ret.begin(), ret.end(), ret.begin(), ::towlower); - return ret; -} - pal::string_t pal::get_timestamp() { std::time_t t = std::time(nullptr); @@ -339,7 +332,7 @@ bool pal::get_dotnet_self_registered_config_location(pal::string_t* recv) const pal::char_t* value; get_dotnet_install_location_registry_path(&key_hive, &sub_key, &value); - *recv = (key_hive == HKEY_CURRENT_USER ? _X("HKCU\\") : _X("HKLM\\")) + sub_key + _X("\\") + value; + recv->assign((key_hive == HKEY_CURRENT_USER ? _X("HKCU\\") : _X("HKLM\\")) + sub_key + _X("\\") + value); return true; #endif } diff --git a/src/native/corehost/hostmisc/utils.cpp b/src/native/corehost/hostmisc/utils.cpp index 055b4f6a31dd8..abf1aeee89e0a 100644 --- a/src/native/corehost/hostmisc/utils.cpp +++ b/src/native/corehost/hostmisc/utils.cpp @@ -161,7 +161,7 @@ void remove_trailing_dir_seperator(pal::string_t* dir) void replace_char(pal::string_t* path, pal::char_t match, pal::char_t repl) { - size_t pos = 0; + size_t pos = 0; while ((pos = path->find(match, pos)) != pal::string_t::npos) { (*path)[pos] = repl; @@ -170,7 +170,7 @@ void replace_char(pal::string_t* path, pal::char_t match, pal::char_t repl) pal::string_t get_replaced_char(const pal::string_t& path, pal::char_t match, pal::char_t repl) { - size_t pos = path.find(match); + size_t pos = path.find(match); if (pos == pal::string_t::npos) { return path; @@ -241,7 +241,7 @@ bool get_env_shared_store_dirs(std::vector* dirs, const pal::stri return true; } -bool get_global_shared_store_dirs(std::vector* dirs, const pal::string_t& arch, const pal::string_t& tfm) +bool get_global_shared_store_dirs(std::vector* dirs, const pal::string_t& arch, const pal::string_t& tfm) { std::vector global_dirs; if (!pal::get_global_dotnet_dirs(&global_dirs)) @@ -348,14 +348,26 @@ bool try_stou(const pal::string_t& str, unsigned* num) return true; } -pal::string_t get_dotnet_root_env_var_name() +bool get_dotnet_root_from_env(pal::string_t* dotnet_root_env_var_name, pal::string_t* recv) { + *dotnet_root_env_var_name = _X("DOTNET_ROOT_"); + dotnet_root_env_var_name->append(to_upper(get_arch())); + if (get_file_path_from_env(dotnet_root_env_var_name->c_str(), recv)) + return true; + +#if defined(WIN32) if (pal::is_running_in_wow64()) { - return pal::string_t(_X("DOTNET_ROOT(x86)")); + *dotnet_root_env_var_name = _X("DOTNET_ROOT(x86)"); + if (get_file_path_from_env(dotnet_root_env_var_name->c_str(), recv)) + return true; } +#endif - return pal::string_t(_X("DOTNET_ROOT")); + // If no architecture-specific environment variable was set + // fallback to the default DOTNET_ROOT. + *dotnet_root_env_var_name = _X("DOTNET_ROOT"); + return get_file_path_from_env(dotnet_root_env_var_name->c_str(), recv); } /** @@ -402,7 +414,7 @@ void get_runtime_config_paths(const pal::string_t& path, const pal::string_t& na trace::verbose(_X("Runtime config is cfg=%s dev=%s"), cfg->c_str(), dev_cfg->c_str()); } -pal::string_t get_dotnet_root_from_fxr_path(const pal::string_t &fxr_path) +pal::string_t get_dotnet_root_from_fxr_path(const pal::string_t& fxr_path) { // If coreclr exists next to hostfxr, assume everything is local (e.g. self-contained) pal::string_t fxr_dir = get_directory(fxr_path); @@ -414,7 +426,7 @@ pal::string_t get_dotnet_root_from_fxr_path(const pal::string_t &fxr_path) return get_directory(get_directory(fxr_root)); } -pal::string_t get_download_url(const pal::char_t *framework_name, const pal::char_t *framework_version) +pal::string_t get_download_url(const pal::char_t* framework_name, const pal::char_t* framework_version) { pal::string_t url = DOTNET_CORE_APPLAUNCH_URL _X("?"); if (framework_name != nullptr && pal::strlen(framework_name) > 0) @@ -441,6 +453,18 @@ pal::string_t get_download_url(const pal::char_t *framework_name, const pal::cha return url; } +pal::string_t to_lower(const pal::char_t* in) { + pal::string_t ret = in; + std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); + return ret; +} + +pal::string_t to_upper(const pal::char_t* in) { + pal::string_t ret = in; + std::transform(ret.begin(), ret.end(), ret.begin(), ::toupper); + return ret; +} + #define TEST_ONLY_MARKER "d38cc827-e34f-4453-9df4-1e796e9f1d07" // Retrieves environment variable which is only used for testing. diff --git a/src/native/corehost/hostmisc/utils.h b/src/native/corehost/hostmisc/utils.h index d0dc381b69cca..b4e31b599fac1 100644 --- a/src/native/corehost/hostmisc/utils.h +++ b/src/native/corehost/hostmisc/utils.h @@ -43,16 +43,19 @@ void get_framework_and_sdk_locations(const pal::string_t& dotnet_dir, std::vecto bool get_file_path_from_env(const pal::char_t* env_key, pal::string_t* recv); size_t index_of_non_numeric(const pal::string_t& str, size_t i); bool try_stou(const pal::string_t& str, unsigned* num); -pal::string_t get_dotnet_root_env_var_name(); +bool get_dotnet_root_from_env(pal::string_t* used_dotnet_root_env_var_name, pal::string_t* recv); pal::string_t get_deps_from_app_binary(const pal::string_t& app_base, const pal::string_t& app); pal::string_t get_runtime_config_path(const pal::string_t& path, const pal::string_t& name); pal::string_t get_runtime_config_dev_path(const pal::string_t& path, const pal::string_t& name); void get_runtime_config_paths(const pal::string_t& path, const pal::string_t& name, pal::string_t* cfg, pal::string_t* dev_cfg); -pal::string_t get_dotnet_root_from_fxr_path(const pal::string_t &fxr_path); +pal::string_t get_dotnet_root_from_fxr_path(const pal::string_t& fxr_path); // Get a download URL for a specific framework and version // If no framework is specified, a download URL for the runtime is returned -pal::string_t get_download_url(const pal::char_t *framework_name = nullptr, const pal::char_t *framework_version = nullptr); +pal::string_t get_download_url(const pal::char_t* framework_name = nullptr, const pal::char_t* framework_version = nullptr); + +pal::string_t to_lower(const pal::char_t* in); +pal::string_t to_upper(const pal::char_t* in); // Retrieves environment variable which is only used for testing. // This will return the value of the variable only if the product binary is stamped @@ -63,7 +66,7 @@ bool test_only_getenv(const pal::char_t* name, pal::string_t* recv); class propagate_error_writer_t { public: - typedef trace::error_writer_fn(__cdecl *set_error_writer_fn)(trace::error_writer_fn error_writer); + typedef trace::error_writer_fn(__cdecl* set_error_writer_fn)(trace::error_writer_fn error_writer); private: set_error_writer_fn m_set_error_writer; diff --git a/src/native/corehost/test/nativehost/nativehost.cpp b/src/native/corehost/test/nativehost/nativehost.cpp index b323f95a82949..80280b7bdbbeb 100644 --- a/src/native/corehost/test/nativehost/nativehost.cpp +++ b/src/native/corehost/test/nativehost/nativehost.cpp @@ -40,7 +40,7 @@ int main(const int argc, const pal::char_t *argv[]) // args: ... [] [] [] [] bool explicit_load = false; if (argc >= 3) - explicit_load = pal::strcmp(pal::to_lower(argv[2]).c_str(), _X("true")) == 0; + explicit_load = pal::strcmp(to_lower(argv[2]).c_str(), _X("true")) == 0; const pal::char_t *assembly_path = nullptr; if (argc >= 4 && pal::strcmp(argv[3], _X("nullptr")) != 0) @@ -117,7 +117,7 @@ int main(const int argc, const pal::char_t *argv[]) if (static_cast(res) == StatusCode::Success) { std::cout << "get_hostfxr_path succeeded" << std::endl; - std::cout << "hostfxr_path: " << tostr(pal::to_lower(fxr_path.c_str())).data() << std::endl; + std::cout << "hostfxr_path: " << tostr(to_lower(fxr_path.c_str())).data() << std::endl; return EXIT_SUCCESS; } else From ea3f4034314481acde910dc571da5006a571dbfa Mon Sep 17 00:00:00 2001 From: Peter Sollich Date: Wed, 23 Jun 2021 12:09:38 +0200 Subject: [PATCH 079/107] Fix finalizer issue with regions (#54550) This fixes an issue in Server GC where an item in the finalizer queue became stale due to not being relocated. The problem was that a finalizable object was allocated on one heap, but registered in the finalizer queue of another heap (this is possible due to heap balancing). In CFinalize::UpdatePromotedGenerations, we ask for the generation of an object, and move the object to the correct section of the finalizer queue. In the error case, we obtained the wrong result for the generation of the object because it lived on another heap, and that heap hadn't set the final generation for the region containing the object yet. So we ended up moving the finalizer entry to the section corresponding to gen 2, and missed a relocation of the object occurring in a gen 1 collection afterwards. Fix: It seems best to make sure an object is always registered for finalization on the heap it's allocated from, so the fix simply fetches the heap from the alloc context after the allocation in the case of SOH, or determines it by calling gc_heap::heap_of in the case of LOH and POH. In the case of SOH, I added an assert to ensure that the heap obtained agrees with the result of calling gc_heap::heap_of. I also added some dprintf calls to the finalizer logic to aid in future investigations. --- src/coreclr/gc/gc.cpp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 6a6c63d1ac29d..9cb439e650411 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -42257,6 +42257,15 @@ GCHeap::Alloc(gc_alloc_context* context, size_t size, uint32_t flags REQD_ALIGN_ newAlloc = (Object*) hp->allocate_uoh_object (size + ComputeMaxStructAlignPadLarge(requiredAlignment), flags, gen_num, acontext->alloc_bytes_uoh); ASSERT(((size_t)newAlloc & 7) == 0); +#ifdef MULTIPLE_HEAPS + if (flags & GC_ALLOC_FINALIZE) + { + // the heap may have changed due to heap balancing - it's important + // to register the object for finalization on the heap it was allocated on + hp = gc_heap::heap_of ((uint8_t*)newAlloc); + } +#endif //MULTIPLE_HEAPS + #ifdef FEATURE_STRUCTALIGN newAlloc = (Object*) hp->pad_for_alignment_large ((uint8_t*) newAlloc, requiredAlignment, size); #endif // FEATURE_STRUCTALIGN @@ -42276,6 +42285,16 @@ GCHeap::Alloc(gc_alloc_context* context, size_t size, uint32_t flags REQD_ALIGN_ newAlloc = (Object*) hp->allocate (size + ComputeMaxStructAlignPad(requiredAlignment), acontext, flags); } +#ifdef MULTIPLE_HEAPS + if (flags & GC_ALLOC_FINALIZE) + { + // the heap may have changed due to heap balancing - it's important + // to register the object for finalization on the heap it was allocated on + hp = acontext->get_alloc_heap()->pGenGCHeap; + assert ((newAlloc == nullptr) || (hp == gc_heap::heap_of ((uint8_t*)newAlloc))); + } +#endif //MULTIPLE_HEAPS + #ifdef FEATURE_STRUCTALIGN newAlloc = (Object*) hp->pad_for_alignment ((uint8_t*) newAlloc, requiredAlignment, size, acontext); #endif // FEATURE_STRUCTALIGN @@ -44404,6 +44423,9 @@ CFinalize::RelocateFinalizationData (int gen, gc_heap* hp) unsigned int Seg = gen_segment (gen); Object** startIndex = SegQueue (Seg); + + dprintf (3, ("RelocateFinalizationData gen=%d, [%Ix,%Ix[", gen, startIndex, SegQueue (FreeList))); + for (Object** po = startIndex; po < SegQueue (FreeList);po++) { GCHeap::Relocate (po, &sc); @@ -44413,6 +44435,8 @@ CFinalize::RelocateFinalizationData (int gen, gc_heap* hp) void CFinalize::UpdatePromotedGenerations (int gen, BOOL gen_0_empty_p) { + dprintf(3, ("UpdatePromotedGenerations gen=%d, gen_0_empty_p=%d", gen, gen_0_empty_p)); + // update the generation fill pointers. // if gen_0_empty is FALSE, test each object to find out if // it was promoted or not @@ -44437,6 +44461,8 @@ CFinalize::UpdatePromotedGenerations (int gen, BOOL gen_0_empty_p) int new_gen = g_theGCHeap->WhichGeneration (*po); if (new_gen != i) { + dprintf (3, ("Moving object %Ix->%Ix from gen %d to gen %d", po, *po, i, new_gen)); + if (new_gen > i) { //promotion @@ -44468,6 +44494,8 @@ CFinalize::GrowArray() } memcpy (newArray, m_Array, oldArraySize*sizeof(Object*)); + dprintf (3, ("Grow finalizer array [%Ix,%Ix[ -> [%Ix,%Ix[", m_Array, m_EndArray, newArray, &m_Array[newArraySize])); + //adjust the fill pointers for (int i = 0; i < FreeList; i++) { From 2b7927faff7d51c577f82f3abfe3bc110cdde1e7 Mon Sep 17 00:00:00 2001 From: Krzysztof Wicher Date: Wed, 23 Jun 2021 14:02:37 +0200 Subject: [PATCH 080/107] Add ILLink annotations to S.D.Common related to DbConnectionStringBuilder (#54280) * Add ILLink annotations to S.D.Common related to DbConnectionStringBuilder * address some feedback * Make GetEvents() safe * make GetProperties safe * Mark GetProperties with RUC --- .../ref/System.Data.Common.cs | 2 + .../src/ILLink/ILLink.Suppressions.xml | 54 ------ .../Data/Common/DbConnectionStringBuilder.cs | 26 ++- .../DbConnectionStringBuilder.cs | 159 ++++++++++++++++++ .../System.Data.Common.TrimmingTests.proj | 5 + 5 files changed, 191 insertions(+), 55 deletions(-) create mode 100644 src/libraries/System.Data.Common/tests/TrimmingTests/DbConnectionStringBuilder.cs diff --git a/src/libraries/System.Data.Common/ref/System.Data.Common.cs b/src/libraries/System.Data.Common/ref/System.Data.Common.cs index a4bc1f651f202..ce7de38a7aefd 100644 --- a/src/libraries/System.Data.Common/ref/System.Data.Common.cs +++ b/src/libraries/System.Data.Common/ref/System.Data.Common.cs @@ -2095,6 +2095,7 @@ protected virtual void OnStateChange(System.Data.StateChangeEventArgs stateChang System.Data.IDbTransaction System.Data.IDbConnection.BeginTransaction(System.Data.IsolationLevel isolationLevel) { throw null; } System.Data.IDbCommand System.Data.IDbConnection.CreateCommand() { throw null; } } + [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] public partial class DbConnectionStringBuilder : System.Collections.ICollection, System.Collections.IDictionary, System.Collections.IEnumerable, System.ComponentModel.ICustomTypeDescriptor { public DbConnectionStringBuilder() { } @@ -2130,6 +2131,7 @@ public virtual void Clear() { } protected internal void ClearPropertyDescriptors() { } public virtual bool ContainsKey(string keyword) { throw null; } public virtual bool EquivalentTo(System.Data.Common.DbConnectionStringBuilder connectionStringBuilder) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered.")] protected virtual void GetProperties(System.Collections.Hashtable propertyDescriptors) { } public virtual bool Remove(string keyword) { throw null; } public virtual bool ShouldSerialize(string keyword) { throw null; } diff --git a/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml index ffcb7d78381c4..3e47be79791de 100644 --- a/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml @@ -103,59 +103,5 @@ member M:System.Data.ProviderBase.SchemaMapping.SetupSchemaWithoutKeyInfo(System.Data.MissingMappingAction,System.Data.MissingSchemaAction,System.Boolean,System.Data.DataColumn,System.Object) - - ILLink - IL2026 - member - M:System.Data.Common.DbConnectionStringBuilder.GetProperties(System.Collections.Hashtable) - - - ILLink - IL2026 - member - M:System.Data.Common.DbConnectionStringBuilder.System#ComponentModel#ICustomTypeDescriptor#GetAttributes - - - ILLink - IL2026 - member - M:System.Data.Common.DbConnectionStringBuilder.System#ComponentModel#ICustomTypeDescriptor#GetClassName - - - ILLink - IL2026 - member - M:System.Data.Common.DbConnectionStringBuilder.System#ComponentModel#ICustomTypeDescriptor#GetComponentName - - - ILLink - IL2026 - member - M:System.Data.Common.DbConnectionStringBuilder.System#ComponentModel#ICustomTypeDescriptor#GetConverter - - - ILLink - IL2026 - member - M:System.Data.Common.DbConnectionStringBuilder.System#ComponentModel#ICustomTypeDescriptor#GetDefaultEvent - - - ILLink - IL2026 - member - M:System.Data.Common.DbConnectionStringBuilder.System#ComponentModel#ICustomTypeDescriptor#GetDefaultProperty - - - ILLink - IL2026 - member - M:System.Data.Common.DbConnectionStringBuilder.System#ComponentModel#ICustomTypeDescriptor#GetEditor(System.Type) - - - ILLink - IL2026 - member - M:System.Data.Common.DbConnectionStringBuilder.System#ComponentModel#ICustomTypeDescriptor#GetEvents - diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilder.cs b/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilder.cs index 3c3ac63548ef8..027fe42f0b08e 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilder.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilder.cs @@ -12,6 +12,7 @@ namespace System.Data.Common { + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] public class DbConnectionStringBuilder : IDictionary, ICustomTypeDescriptor { // keyword->value currently listed in the connection string @@ -387,6 +388,7 @@ internal Attribute[] GetAttributesFromCollection(AttributeCollection collection) return attributes; } + [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered.")] private PropertyDescriptorCollection GetProperties() { PropertyDescriptorCollection? propertyDescriptors = _propertyDescriptors; @@ -412,11 +414,16 @@ private PropertyDescriptorCollection GetProperties() return propertyDescriptors; } + [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered.")] protected virtual void GetProperties(Hashtable propertyDescriptors) { long logScopeId = DataCommonEventSource.Log.EnterScope(" {0}", ObjectID); try { + // Below call is necessary to tell the trimmer that it should mark derived types appropriately. + // We cannot use overload which takes type because the result might differ if derived class implements ICustomTypeDescriptor. + Type thisType = GetType(); + // show all strongly typed properties (not already added) // except ConnectionString iff BrowsableConnectionString Attribute[]? attributes; @@ -562,16 +569,28 @@ private PropertyDescriptorCollection GetProperties(Attribute[]? attributes) return new PropertyDescriptorCollection(filteredPropertiesArray); } -// TODO: Enable after System.ComponentModel.TypeConverter is annotated + // TODO-NULLABLE: Enable after System.ComponentModel.TypeConverter is annotated #nullable disable + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "The type of component is statically known. This class is marked with [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]")] string ICustomTypeDescriptor.GetClassName() { + // Below call is necessary to tell the trimmer that it should mark derived types appropriately. + // We cannot use overload which takes type because the result might differ if derived class implements ICustomTypeDescriptor. + Type thisType = GetType(); return TypeDescriptor.GetClassName(this, true); } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "The type of component is statically known. This class is marked with [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]")] string ICustomTypeDescriptor.GetComponentName() { + // Below call is necessary to tell the trimmer that it should mark derived types appropriately. + // We cannot use overload which takes type because the result might differ if derived class implements ICustomTypeDescriptor. + Type thisType = GetType(); return TypeDescriptor.GetComponentName(this, true); } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "The type of component is statically known. This class is marked with [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]")] AttributeCollection ICustomTypeDescriptor.GetAttributes() { return TypeDescriptor.GetAttributes(this, true); @@ -606,8 +625,13 @@ EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() { return TypeDescriptor.GetDefaultEvent(this, true); } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "The type of component is statically known. This class is marked with [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]")] EventDescriptorCollection ICustomTypeDescriptor.GetEvents() { + // Below call is necessary to tell the trimmer that it should mark derived types appropriately. + // We cannot use overload which takes type because the result might differ if derived class implements ICustomTypeDescriptor. + Type thisType = GetType(); return TypeDescriptor.GetEvents(this, true); } [RequiresUnreferencedCode("The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] diff --git a/src/libraries/System.Data.Common/tests/TrimmingTests/DbConnectionStringBuilder.cs b/src/libraries/System.Data.Common/tests/TrimmingTests/DbConnectionStringBuilder.cs new file mode 100644 index 0000000000000..78ac56e9f751a --- /dev/null +++ b/src/libraries/System.Data.Common/tests/TrimmingTests/DbConnectionStringBuilder.cs @@ -0,0 +1,159 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Data.Common; +using System.ComponentModel; +using System.Collections; + +namespace DbConnectionStringBuilderTrimmingTests +{ + class Program + { + static int Main(string[] args) + { + DbConnectionStringBuilder2 dcsb2 = new(); + ICustomTypeDescriptor td = dcsb2; + + if (td.GetClassName() != "DbConnectionStringBuilderTrimmingTests.DbConnectionStringBuilder2") + { + throw new Exception("Class name got trimmed"); + } + + if (td.GetComponentName() != "Test Component Name") + { + throw new Exception("Component name got trimmed"); + } + + bool foundAttr = false; + + foreach (Attribute attr in td.GetAttributes()) + { + if (attr.GetType().Name == "TestAttribute") + { + if (attr.ToString() != "Test Attribute Value") + { + throw new Exception("Test attribute value differs"); + } + + if (foundAttr) + { + throw new Exception("More than one attribute found"); + } + + foundAttr = true; + } + } + + if (!foundAttr) + { + throw new Exception("Attribute not found"); + } + + bool foundEvent = false; + bool foundEventWithDisplayName = false; + + foreach (EventDescriptor ev in td.GetEvents()) + { + if (ev.DisplayName == "TestEvent") + { + if (foundEvent) + { + throw new Exception("More than one event TestEvent found."); + } + + foundEvent = true; + } + + if (ev.DisplayName == "Event With DisplayName") + { + if (foundEventWithDisplayName) + { + throw new Exception("More than one event with display name found."); + } + + foundEventWithDisplayName = true; + } + } + + if (!foundEvent) + { + throw new Exception("Event not found"); + } + + if (!foundEventWithDisplayName) + { + throw new Exception("Event with DisplayName not found"); + } + + bool propertyFound = false; + foreach (DictionaryEntry kv in dcsb2.GetProperties2()) + { + PropertyDescriptor val = (PropertyDescriptor)kv.Value; + if (val.Name == "TestProperty") + { + if (propertyFound) + { + throw new Exception("More than one property TestProperty found."); + } + + propertyFound = true; + } + } + + if (!propertyFound) + { + throw new Exception("Property not found"); + } + + return 100; + } + } + + [Test("Test Attribute Value")] + class DbConnectionStringBuilder2 : DbConnectionStringBuilder, IComponent + { +#pragma warning disable CS0067 // The event is never used + public event EventHandler Disposed; + public event Action TestEvent; + [DisplayName("Event With DisplayName")] + public event Action TestEvent2; +#pragma warning restore CS0067 + + public string TestProperty { get; set; } + public ISite Site { get => new TestSite(); set => throw new NotImplementedException(); } + public void Dispose() { } + + public Hashtable GetProperties2() + { + Hashtable propertyDescriptors = new Hashtable(); + GetProperties(propertyDescriptors); + return propertyDescriptors; + } + } + + class TestSite : INestedSite + { + public string FullName => null; + public IComponent Component => throw new NotImplementedException(); + public IContainer Container => throw new NotImplementedException(); + public bool DesignMode => throw new NotImplementedException(); + public string Name { get => "Test Component Name"; set => throw new NotImplementedException(); } + public object GetService(Type serviceType) => null; + } + + class TestAttribute : Attribute + { + public string Test { get; private set; } + + public TestAttribute(string test) + { + Test = test; + } + + public override string ToString() + { + return Test; + } + } +} diff --git a/src/libraries/System.Data.Common/tests/TrimmingTests/System.Data.Common.TrimmingTests.proj b/src/libraries/System.Data.Common/tests/TrimmingTests/System.Data.Common.TrimmingTests.proj index da4a46f2ae141..7254c33fc9c7b 100644 --- a/src/libraries/System.Data.Common/tests/TrimmingTests/System.Data.Common.TrimmingTests.proj +++ b/src/libraries/System.Data.Common/tests/TrimmingTests/System.Data.Common.TrimmingTests.proj @@ -1,5 +1,10 @@ + + + + + From 56b3751de114202a44324014196102efc3102775 Mon Sep 17 00:00:00 2001 From: Lakshan Fernando Date: Wed, 23 Jun 2021 05:11:42 -0700 Subject: [PATCH 081/107] Root ComActivator for hosting (#54524) * rooting ComActivator that is needed for hosting * FB --- .../src/ILLink/ILLink.Descriptors.Windows.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Windows.xml b/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Windows.xml index 0648aed9763d1..52ab4af9bef74 100644 --- a/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Windows.xml +++ b/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Windows.xml @@ -4,5 +4,11 @@ + + + + + + From 9039c7764e989d97e59db4982cb2b73c5179b471 Mon Sep 17 00:00:00 2001 From: Marek Safar Date: Wed, 23 Jun 2021 14:56:44 +0200 Subject: [PATCH 082/107] Respect EventSource::IsSupported setting in more codepaths (#51977) --- .../src/System/Diagnostics/Tracing/EventSource.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs index 321d60de739b3..d6093b4fb66bb 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs @@ -495,6 +495,9 @@ public static void SendCommand(EventSource eventSource, EventCommand command, ID /// public override string ToString() { + if (!IsSupported) + return base.ToString()!; + return SR.Format(SR.EventSource_ToString, Name, Guid); } From fa86c81b61b10daf3da4438b894a7d4405e229e8 Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Wed, 23 Jun 2021 09:38:02 -0400 Subject: [PATCH 083/107] Make mono_polling_required a public symbol (#54592) Resolves build failure with the Android aot+llvm functional test --- src/mono/mono/utils/mono-threads-coop.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/mono/utils/mono-threads-coop.h b/src/mono/mono/utils/mono-threads-coop.h index 47c20c140253a..975915812946a 100644 --- a/src/mono/mono/utils/mono-threads-coop.h +++ b/src/mono/mono/utils/mono-threads-coop.h @@ -20,7 +20,7 @@ #include "mono/metadata/icalls.h" /* JIT specific interface */ -extern volatile size_t mono_polling_required; +MONO_API_DATA volatile size_t mono_polling_required; /* Internal API */ From c0ed319ba3164bfbd9c2c4d66f68f29f8cd78b04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksey=20Kliger=20=28=CE=BBgeek=29?= Date: Wed, 23 Jun 2021 09:44:41 -0400 Subject: [PATCH 084/107] Change PathInternal.IsCaseSensitive to a constant (#54340) * Return constants in PathInternal.GetIsCaseSensitive() on mobile platforms In particular on Android probing using I/O is slow and contributes to slow app startup. Fixes https://github.com/dotnet/runtime/issues/54339 * Implement Path.IsCaseSensitive as PathInternal.IsCaseSensitive Also Path.StringComparison => PathInternal.StringComparison * Add test for PathInternal.IsCaseSensitive Move GetIsCaseSensitiveByProbing to FileSystemTest * Drop PathInternal.s_isCaseSensitive cache field * Delete Path.IsCaseSensitive and Path.StringComparison update callers to use PathInternal.IsCaseSensitive and PathInternal.StringComparison * Remove catch clause from GetIsCaseSensitiveByProbing * Mark new test [OuterLoop] * Apply suggestions from code review Co-authored-by: Stephen Toub Co-authored-by: Jan Kotas Co-authored-by: Adam Sitnik Co-authored-by: Stephen Toub --- .../System/IO/PathInternal.CaseSensitivity.cs | 36 +++++-------------- .../tests/FileSystemTest.cs | 19 ++++++++++ ...stem.IO.FileSystem.Net5Compat.Tests.csproj | 2 ++ .../tests/PathInternalTests.cs | 18 ++++++++++ .../tests/System.IO.FileSystem.Tests.csproj | 3 ++ .../src/System/IO/Path.Unix.cs | 12 ------- .../src/System/IO/Path.Windows.cs | 3 -- .../src/System/IO/Path.cs | 8 +---- .../Runtime/Loader/AssemblyLoadContext.cs | 2 +- 9 files changed, 52 insertions(+), 51 deletions(-) create mode 100644 src/libraries/System.IO.FileSystem/tests/PathInternalTests.cs diff --git a/src/libraries/Common/src/System/IO/PathInternal.CaseSensitivity.cs b/src/libraries/Common/src/System/IO/PathInternal.CaseSensitivity.cs index 5611e9c696987..3e3486157e841 100644 --- a/src/libraries/Common/src/System/IO/PathInternal.CaseSensitivity.cs +++ b/src/libraries/Common/src/System/IO/PathInternal.CaseSensitivity.cs @@ -12,47 +12,27 @@ namespace System.IO /// Contains internal path helpers that are shared between many projects. internal static partial class PathInternal { - private static readonly bool s_isCaseSensitive = GetIsCaseSensitive(); - /// Returns a comparison that can be used to compare file and directory names for equality. internal static StringComparison StringComparison { get { - return s_isCaseSensitive ? + return IsCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; } } /// Gets whether the system is case-sensitive. - internal static bool IsCaseSensitive { get { return s_isCaseSensitive; } } - - /// - /// Determines whether the file system is case sensitive. - /// - /// - /// Ideally we'd use something like pathconf with _PC_CASE_SENSITIVE, but that is non-portable, - /// not supported on Windows or Linux, etc. For now, this function creates a tmp file with capital letters - /// and then tests for its existence with lower-case letters. This could return invalid results in corner - /// cases where, for example, different file systems are mounted with differing sensitivities. - /// - private static bool GetIsCaseSensitive() + internal static bool IsCaseSensitive { - try - { - string pathWithUpperCase = Path.Combine(Path.GetTempPath(), "CASESENSITIVETEST" + Guid.NewGuid().ToString("N")); - using (new FileStream(pathWithUpperCase, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, 0x1000, FileOptions.DeleteOnClose)) - { - string lowerCased = pathWithUpperCase.ToLowerInvariant(); - return !File.Exists(lowerCased); - } - } - catch + get { - // In case something goes wrong (e.g. temp pointing to a privilieged directory), we don't - // want to fail just because of a casing test, so we assume case-insensitive-but-preserving. - return false; +#if MS_IO_REDIST + return false; // Windows is always case-insensitive +#else + return !(OperatingSystem.IsWindows() || OperatingSystem.IsMacOS() || OperatingSystem.IsMacCatalyst() || OperatingSystem.IsIOS() || OperatingSystem.IsTvOS() || OperatingSystem.IsWatchOS()); +#endif } } } diff --git a/src/libraries/System.IO.FileSystem/tests/FileSystemTest.cs b/src/libraries/System.IO.FileSystem/tests/FileSystemTest.cs index 2ba15d31b4144..d82346f6f4f10 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileSystemTest.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileSystemTest.cs @@ -127,5 +127,24 @@ protected void ReadOnly_FileSystemHelper(Action testAction, string subDi Assert.Equal(0, AdminHelpers.RunAsSudo($"umount {readOnlyDirectory}")); } } + + /// + /// Determines whether the file system is case sensitive by creating a file in the specified folder and observing the result. + /// + /// + /// Ideally we'd use something like pathconf with _PC_CASE_SENSITIVE, but that is non-portable, + /// not supported on Windows or Linux, etc. For now, this function creates a tmp file with capital letters + /// and then tests for its existence with lower-case letters. This could return invalid results in corner + /// cases where, for example, different file systems are mounted with differing sensitivities. + /// + protected static bool GetIsCaseSensitiveByProbing(string probingDirectory) + { + string pathWithUpperCase = Path.Combine(probingDirectory, "CASESENSITIVETEST" + Guid.NewGuid().ToString("N")); + using (new FileStream(pathWithUpperCase, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, 0x1000, FileOptions.DeleteOnClose)) + { + string lowerCased = pathWithUpperCase.ToLowerInvariant(); + return !File.Exists(lowerCased); + } + } } } diff --git a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj index 628bbb4324aea..58669657e51b1 100644 --- a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj +++ b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj @@ -37,6 +37,8 @@ + diff --git a/src/libraries/System.IO.FileSystem/tests/PathInternalTests.cs b/src/libraries/System.IO.FileSystem/tests/PathInternalTests.cs new file mode 100644 index 0000000000000..c908601862eea --- /dev/null +++ b/src/libraries/System.IO.FileSystem/tests/PathInternalTests.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; + +namespace System.IO.Tests +{ + public class PathInternalTests : FileSystemTest + { + [Fact] + [OuterLoop] + public void PathInternalIsCaseSensitiveMatchesProbing() + { + string probingDirectory = TestDirectory; + Assert.Equal(GetIsCaseSensitiveByProbing(probingDirectory), PathInternal.IsCaseSensitive); + } + } +} diff --git a/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj b/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj index a271109ce2fc6..94cec86ce3a9a 100644 --- a/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj +++ b/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj @@ -51,6 +51,7 @@ + @@ -191,6 +192,8 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs index ecc990b07c6e1..5a7f0c8d1bbe5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs @@ -129,17 +129,5 @@ public static ReadOnlySpan GetPathRoot(ReadOnlySpan path) return IsPathRooted(path) ? PathInternal.DirectorySeparatorCharAsString.AsSpan() : ReadOnlySpan.Empty; } - /// Gets whether the system is case-sensitive. - internal static bool IsCaseSensitive - { - get - { - #if TARGET_OSX || TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS - return false; - #else - return true; - #endif - } - } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs index 5e641806926b8..5f4b8e11a1920 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs @@ -230,9 +230,6 @@ public static ReadOnlySpan GetPathRoot(ReadOnlySpan path) return pathRoot <= 0 ? ReadOnlySpan.Empty : path.Slice(0, pathRoot); } - /// Gets whether the system is case-sensitive. - internal static bool IsCaseSensitive => false; - /// /// Returns the volume name for dos, UNC and device paths. /// diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs index 7601e45e074d1..104704de7b715 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs @@ -860,7 +860,7 @@ private static unsafe void Populate83FileNameFromRandomBytes(byte* bytes, int by /// Thrown if or is null or an empty string. public static string GetRelativePath(string relativeTo, string path) { - return GetRelativePath(relativeTo, path, StringComparison); + return GetRelativePath(relativeTo, path, PathInternal.StringComparison); } private static string GetRelativePath(string relativeTo, string path, StringComparison comparisonType) @@ -957,12 +957,6 @@ private static string GetRelativePath(string relativeTo, string path, StringComp return sb.ToString(); } - /// Returns a comparison that can be used to compare file and directory names for equality. - internal static StringComparison StringComparison => - IsCaseSensitive ? - StringComparison.Ordinal : - StringComparison.OrdinalIgnoreCase; - /// /// Trims one trailing directory separator beyond the root of the path. /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs index 6c733ef6ca150..2a5a3c1da07fb 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs @@ -781,7 +781,7 @@ private static void OnAssemblyLoad(RuntimeAssembly assembly) string assemblyPath = Path.Combine(parentDirectory, assemblyName.CultureName!, $"{assemblyName.Name}.dll"); bool exists = System.IO.FileSystem.FileExists(assemblyPath); - if (!exists && Path.IsCaseSensitive) + if (!exists && PathInternal.IsCaseSensitive) { #if CORECLR if (AssemblyLoadContext.IsTracingEnabled()) From 0416c3469b7bccff087b89db3d9967dfd36070a3 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Wed, 23 Jun 2021 12:53:56 -0400 Subject: [PATCH 085/107] [wasm] Compile .bc->.o in parallel, before passing to the linker (#54053) --- eng/Versions.props | 2 +- src/libraries/tests.proj | 5 + src/mono/wasm/build/WasmApp.Native.targets | 76 ++++++--- src/tasks/AotCompilerTask/MonoAOTCompiler.cs | 13 +- src/tasks/Common/Utils.cs | 110 ++++++++++-- src/tasks/WasmAppBuilder/EmccCompile.cs | 158 ++++++++++++++++++ src/tasks/WasmAppBuilder/WasmAppBuilder.cs | 5 - .../WasmAppBuilder/WasmAppBuilder.csproj | 2 + 8 files changed, 323 insertions(+), 48 deletions(-) create mode 100644 src/tasks/WasmAppBuilder/EmccCompile.cs diff --git a/eng/Versions.props b/eng/Versions.props index 2295b6e440d51..a194baa33def9 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -140,7 +140,7 @@ These are used as reference assemblies only, so they must not take a ProdCon/source-build version. Insert "RefOnly" to avoid assignment via PVP. --> - 16.8.0 + 16.9.0 $(RefOnlyMicrosoftBuildVersion) $(RefOnlyMicrosoftBuildVersion) $(RefOnlyMicrosoftBuildVersion) diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index b1ca6c3a1ddba..5cad3c540880b 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -255,6 +255,8 @@ + + @@ -276,6 +278,9 @@ + + + diff --git a/src/mono/wasm/build/WasmApp.Native.targets b/src/mono/wasm/build/WasmApp.Native.targets index b6897fbd2b413..c034763ca0f25 100644 --- a/src/mono/wasm/build/WasmApp.Native.targets +++ b/src/mono/wasm/build/WasmApp.Native.targets @@ -3,6 +3,7 @@ + <_WasmBuildNativeCoreDependsOn> @@ -165,7 +166,7 @@ <_EmccOptimizationFlagDefault Condition="'$(_EmccOptimizationFlagDefault)' == ''">-Oz $(_EmccOptimizationFlagDefault) - $(_EmccOptimizationFlagDefault) + -O0 @@ -173,10 +174,6 @@ <_EmccCommonFlags Include="$(EmccFlags)" /> <_EmccCommonFlags Include="-s DISABLE_EXCEPTION_CATCHING=0" /> <_EmccCommonFlags Include="-g" Condition="'$(WasmNativeStrip)' == 'false'" /> - <_EmccCommonFlags Include="-DENABLE_AOT=1" Condition="'$(RunAOTCompilation)' == 'true'" /> - <_EmccCommonFlags Include="-DDRIVER_GEN=1" Condition="'$(RunAOTCompilation)' == 'true'" /> - <_EmccCommonFlags Include="-DINVARIANT_GLOBALIZATION=1" Condition="'$(InvariantGlobalization)' == 'true'" /> - <_EmccCommonFlags Include="-DLINK_ICALLS=1" Condition="'$(WasmLinkIcalls)' == 'true'" /> <_EmccCommonFlags Include="-v" Condition="'$(EmccVerbose)' != 'false'" /> @@ -186,11 +183,7 @@ - <_WasmObjectsToBuild Include="$(_WasmRuntimePackSrcDir)\*.c" /> - <_WasmObjectsToBuild OutputPath="$(_WasmIntermediateOutputPath)%(FileName).o" /> - <_DotnetJSSrcFile Include="$(_WasmRuntimePackSrcDir)\*.js" /> - <_WasmPInvokeModules Include="%(_WasmNativeFileForLinking.FileName)" /> @@ -227,54 +220,84 @@ <_EmccCFlags Include="$(EmccCompileOptimizationFlag)" /> <_EmccCFlags Include="@(_EmccCommonFlags)" /> + + <_EmccCFlags Include="-DENABLE_AOT=1" Condition="'$(RunAOTCompilation)' == 'true'" /> + <_EmccCFlags Include="-DDRIVER_GEN=1" Condition="'$(RunAOTCompilation)' == 'true'" /> + <_EmccCFlags Include="-DINVARIANT_GLOBALIZATION=1" Condition="'$(InvariantGlobalization)' == 'true'" /> + <_EmccCFlags Include="-DLINK_ICALLS=1" Condition="'$(WasmLinkIcalls)' == 'true'" /> <_EmccCFlags Include="-DCORE_BINDINGS" /> <_EmccCFlags Include="-DGEN_PINVOKE=1" /> + <_EmccCFlags Include=""-I%(_EmccIncludePaths.Identity)"" /> <_EmccCFlags Include="-g" Condition="'$(WasmNativeDebugSymbols)' == 'true'" /> <_EmccCFlags Include="-s EXPORTED_FUNCTIONS='[@(_ExportedFunctions->'"%(Identity)"', ',')]'" Condition="@(_ExportedFunctions->Count()) > 0" /> <_EmccCFlags Include="$(EmccExtraCFlags)" /> + + <_WasmRuntimePackSrcFile Remove="@(_WasmRuntimePackSrcFile)" /> + <_WasmRuntimePackSrcFile Include="$(_WasmRuntimePackSrcDir)\*.c" /> + <_WasmRuntimePackSrcFile ObjectFile="$(_WasmIntermediateOutputPath)%(FileName).o" /> + <_WasmSourceFileToCompile Include="@(_WasmRuntimePackSrcFile)" /> + <_EmBuilder Condition="$([MSBuild]::IsOSPlatform('WINDOWS'))">embuilder.bat + <_EmBuilder Condition="!$([MSBuild]::IsOSPlatform('WINDOWS'))">embuilder.py <_EmccCompileRsp>$(_WasmIntermediateOutputPath)emcc-compile.rsp + + + - + - <_WasmRuntimePackNativeLibs Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)\*.a" /> - <_WasmObjects Include="@(_WasmRuntimePackNativeLibs)" /> - <_WasmObjects Include="@(_WasmObjectsToBuild->'%(OutputPath)')" /> - <_EmccLDFlags Include="$(EmccLinkOptimizationFlag)" /> <_EmccLDFlags Include="@(_EmccCommonFlags)" /> + <_EmccLDFlags Include="-s TOTAL_MEMORY=536870912" /> <_EmccLDFlags Include="$(EmccExtraLDFlags)" /> + + + + + + + <_WasmNativeFileForLinking Include="%(_BitcodeFile.ObjectFile)" /> + <_WasmNativeFileForLinking Include="%(_WasmSourceFileToCompile.ObjectFile)" /> - <_EmccLDFlags Include="--js-library "%(_DotnetJSSrcFile.Identity)"" /> - <_EmccLDFlags Include="--js-library "%(_WasmExtraJSFile.Identity)"" Condition="'%(_WasmExtraJSFile.Kind)' == 'js-library'" /> + + <_WasmNativeFileForLinking Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)\*.a" /> - <_EmccLDFlags Include="--pre-js "%(_WasmExtraJSFile.Identity)"" Condition="'%(_WasmExtraJSFile.Kind)' == 'pre-js'" /> - <_EmccLDFlags Include="--post-js "%(_WasmExtraJSFile.Identity)"" Condition="'%(_WasmExtraJSFile.Kind)' == 'post-js'" /> + <_EmccLinkStepArgs Include="@(_EmccLDFlags)" /> + <_EmccLinkStepArgs Include="--js-library "%(_DotnetJSSrcFile.Identity)"" /> + <_EmccLinkStepArgs Include="--js-library "%(_WasmExtraJSFile.Identity)"" Condition="'%(_WasmExtraJSFile.Kind)' == 'js-library'" /> - <_EmccLDFlags Include=""%(_WasmNativeFileForLinking.Identity)"" /> - <_EmccLDFlags Include=""%(_WasmObjects.Identity)"" /> - <_EmccLDFlags Include="-o "$(_WasmIntermediateOutputPath)dotnet.js"" /> + <_EmccLinkStepArgs Include="--pre-js "%(_WasmExtraJSFile.Identity)"" Condition="'%(_WasmExtraJSFile.Kind)' == 'pre-js'" /> + <_EmccLinkStepArgs Include="--post-js "%(_WasmExtraJSFile.Identity)"" Condition="'%(_WasmExtraJSFile.Kind)' == 'post-js'" /> + + <_EmccLinkStepArgs Include=""%(_WasmNativeFileForLinking.Identity)"" /> + <_EmccLinkStepArgs Include="-o "$(_WasmIntermediateOutputPath)dotnet.js"" /> <_EmccLinkRsp>$(_WasmIntermediateOutputPath)emcc-link.rsp - - + + + + @@ -289,7 +312,7 @@ - $(EmccFlags) -DDRIVER_GEN=1 + $(EmccExtraCFlags) -DDRIVER_GEN=1 void mono_profiler_init_aot (const char *desc)%3B EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_profiler_init_aot (desc)%3B } @@ -405,7 +428,7 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_ Profilers="$(WasmProfilers)" AotModulesTablePath="$(_WasmIntermediateOutputPath)driver-gen.c" UseLLVM="true" - DisableParallelAot="true" + DisableParallelAot="$(DisableParallelAot)" DedupAssembly="$(_WasmDedupAssembly)" LLVMDebug="dwarfdebug" LLVMPath="$(EmscriptenUpstreamBinPath)" > @@ -420,8 +443,7 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_ <_AOTAssemblies Include="@(_WasmAssembliesInternal)" Condition="'%(_WasmAssembliesInternal._InternalForceInterpret)' != 'true'" /> <_BitcodeFile Include="%(_WasmAssembliesInternal.LlvmBitcodeFile)" /> - - <_WasmNativeFileForLinking Include="@(_BitcodeFile)" /> + <_BitcodeFile ObjectFile="$(_WasmIntermediateOutputPath)%(FileName).o" /> diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs index 52d37fe900e0f..cb3876cfc9c36 100644 --- a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs +++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs @@ -417,8 +417,17 @@ private bool PrecompileLibrary(ITaskItem assemblyItem, string? monoPaths) try { // run the AOT compiler - Utils.RunProcess(CompilerBinaryPath, string.Join(" ", processArgs), envVariables, assemblyDir, silent: false, - outputMessageImportance: MessageImportance.Low, debugMessageImportance: MessageImportance.Low); + (int exitCode, string output) = Utils.TryRunProcess(CompilerBinaryPath, + string.Join(" ", processArgs), + envVariables, + assemblyDir, + silent: false, + debugMessageImportance: MessageImportance.Low); + if (exitCode != 0) + { + Log.LogError($"Precompiling failed for {assembly}: {output}"); + return false; + } } catch (Exception ex) { diff --git a/src/tasks/Common/Utils.cs b/src/tasks/Common/Utils.cs index 696ef14d85e80..ea2aba607dd8e 100644 --- a/src/tasks/Common/Utils.cs +++ b/src/tasks/Common/Utils.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Runtime.InteropServices; using System.Text; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; @@ -21,6 +22,49 @@ public static string GetEmbeddedResource(string file) return reader.ReadToEnd(); } + public static (int exitCode, string output) RunShellCommand(string command, + IDictionary envVars, + string workingDir, + MessageImportance debugMessageImportance=MessageImportance.Low) + { + string scriptFileName = CreateTemporaryBatchFile(command); + (string shell, string args) = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? ("cmd", $"/c \"{scriptFileName}\"") + : ("/bin/sh", $"\"{scriptFileName}\""); + + Logger?.LogMessage(debugMessageImportance, $"Running {command} via script {scriptFileName}:"); + Logger?.LogMessage(debugMessageImportance, File.ReadAllText(scriptFileName)); + + return TryRunProcess(shell, + args, + envVars, + workingDir, + silent: false, + debugMessageImportance); + + static string CreateTemporaryBatchFile(string command) + { + string extn = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".cmd" : ".sh"; + string file = Path.Combine(Path.GetTempPath(), $"tmp{Guid.NewGuid():N}{extn}"); + + using StreamWriter sw = new(file); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + sw.WriteLine("setlocal"); + sw.WriteLine("set errorlevel=dummy"); + sw.WriteLine("set errorlevel="); + } + else + { + // Use sh rather than bash, as not all 'nix systems necessarily have Bash installed + sw.WriteLine("#!/bin/sh"); + } + + sw.WriteLine(command); + return file; + } + } + public static string RunProcess( string path, string args = "", @@ -28,10 +72,31 @@ public static string RunProcess( string? workingDir = null, bool ignoreErrors = false, bool silent = true, - MessageImportance outputMessageImportance=MessageImportance.High, MessageImportance debugMessageImportance=MessageImportance.High) { - LogInfo($"Running: {path} {args}", debugMessageImportance); + (int exitCode, string output) = TryRunProcess( + path, + args, + envVars, + workingDir, + silent, + debugMessageImportance); + + if (exitCode != 0 && !ignoreErrors) + throw new Exception("Error: Process returned non-zero exit code: " + output); + + return output; + } + + public static (int, string) TryRunProcess( + string path, + string args = "", + IDictionary? envVars = null, + string? workingDir = null, + bool silent = true, + MessageImportance debugMessageImportance=MessageImportance.High) + { + Logger?.LogMessage(debugMessageImportance, $"Running: {path} {args}"); var outputBuilder = new StringBuilder(); var processStartInfo = new ProcessStartInfo { @@ -46,7 +111,7 @@ public static string RunProcess( if (workingDir != null) processStartInfo.WorkingDirectory = workingDir; - LogInfo($"Using working directory: {workingDir ?? Environment.CurrentDirectory}", debugMessageImportance); + Logger?.LogMessage(debugMessageImportance, $"Using working directory: {workingDir ?? Environment.CurrentDirectory}"); if (envVars != null) { @@ -68,10 +133,11 @@ public static string RunProcess( { lock (s_SyncObj) { + if (string.IsNullOrEmpty(e.Data)) + return; + if (!silent) - { LogWarning(e.Data); - } outputBuilder.AppendLine(e.Data); } }; @@ -79,10 +145,11 @@ public static string RunProcess( { lock (s_SyncObj) { + if (string.IsNullOrEmpty(e.Data)) + return; + if (!silent) - { - LogInfo(e.Data, outputMessageImportance); - } + Logger?.LogMessage(debugMessageImportance, e.Data); outputBuilder.AppendLine(e.Data); } }; @@ -90,14 +157,31 @@ public static string RunProcess( process.BeginErrorReadLine(); process.WaitForExit(); - if (process.ExitCode != 0) + Logger?.LogMessage(debugMessageImportance, $"Exit code: {process.ExitCode}"); + return (process.ExitCode, outputBuilder.ToString().Trim('\r', '\n')); + } + + internal static string CreateTemporaryBatchFile(string command) + { + string extn = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".cmd" : ".sh"; + string file = Path.Combine(Path.GetTempPath(), $"tmp{Guid.NewGuid():N}{extn}"); + + using StreamWriter sw = new(file); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - Logger?.LogMessage(MessageImportance.High, $"Exit code: {process.ExitCode}"); - if (!ignoreErrors) - throw new Exception("Error: Process returned non-zero exit code: " + outputBuilder); + sw.WriteLine("setlocal"); + sw.WriteLine("set errorlevel=dummy"); + sw.WriteLine("set errorlevel="); } + else + { + // Use sh rather than bash, as not all 'nix systems necessarily have Bash installed + sw.WriteLine("#!/bin/sh"); + } + + sw.WriteLine(command); - return silent ? string.Empty : outputBuilder.ToString().Trim('\r', '\n'); + return file; } #if NETCOREAPP diff --git a/src/tasks/WasmAppBuilder/EmccCompile.cs b/src/tasks/WasmAppBuilder/EmccCompile.cs new file mode 100644 index 0000000000000..b175c62c2ee11 --- /dev/null +++ b/src/tasks/WasmAppBuilder/EmccCompile.cs @@ -0,0 +1,158 @@ +// 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.Concurrent; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +#nullable enable + +namespace Microsoft.WebAssembly.Build.Tasks +{ + /// + /// This is meant to *compile* source files only. It is *not* a general purpose + /// `emcc` invocation task. + /// + /// It runs `emcc` for each source file, and with output to `%(SourceFiles.ObjectFile)` + /// + /// + public class EmccCompile : Microsoft.Build.Utilities.Task + { + [NotNull] + [Required] + public ITaskItem[]? SourceFiles { get; set; } + + public ITaskItem[]? EnvironmentVariables { get; set; } + public bool DisableParallelCompile { get; set; } + public string Arguments { get; set; } = string.Empty; + public string? WorkingDirectory { get; set; } + + [Output] + public ITaskItem[]? OutputFiles { get; private set; } + + private string? _tempPath; + + public override bool Execute() + { + if (SourceFiles.Length == 0) + { + Log.LogError($"No SourceFiles to compile"); + return false; + } + + ITaskItem? badItem = SourceFiles.FirstOrDefault(sf => string.IsNullOrEmpty(sf.GetMetadata("ObjectFile"))); + if (badItem != null) + { + Log.LogError($"Source file {badItem.ItemSpec} is missing ObjectFile metadata."); + return false; + } + + IDictionary envVarsDict = GetEnvironmentVariablesDict(); + ConcurrentBag outputItems = new(); + try + { + Log.LogMessage(MessageImportance.Low, "Using environment variables:"); + foreach (var kvp in envVarsDict) + Log.LogMessage(MessageImportance.Low, $"\t{kvp.Key} = {kvp.Value}"); + + string workingDir = Environment.CurrentDirectory; + Log.LogMessage(MessageImportance.Low, $"Using working directory: {workingDir}"); + + _tempPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + Directory.CreateDirectory(_tempPath); + + int allowedParallelism = Math.Min(SourceFiles.Length, Environment.ProcessorCount); +#if false // Enable this when we bump msbuild to 16.1.0 + if (BuildEngine is IBuildEngine9 be9) + allowedParallelism = be9.RequestCores(allowedParallelism); +#endif + + if (DisableParallelCompile || allowedParallelism == 1) + { + foreach (ITaskItem srcItem in SourceFiles) + { + if (!ProcessSourceFile(srcItem)) + return false; + } + } + else + { + ParallelLoopResult result = Parallel.ForEach(SourceFiles, + new ParallelOptions { MaxDegreeOfParallelism = allowedParallelism }, + (srcItem, state) => + { + if (!ProcessSourceFile(srcItem)) + state.Stop(); + }); + + if (!result.IsCompleted && !Log.HasLoggedErrors) + Log.LogError("Unknown failed occured while compiling"); + } + } + finally + { + if (!string.IsNullOrEmpty(_tempPath)) + Directory.Delete(_tempPath, true); + } + + OutputFiles = outputItems.ToArray(); + return !Log.HasLoggedErrors; + + bool ProcessSourceFile(ITaskItem srcItem) + { + string srcFile = srcItem.ItemSpec; + string objFile = srcItem.GetMetadata("ObjectFile"); + + try + { + string command = $"emcc {Arguments} -c -o {objFile} {srcFile}"; + (int exitCode, string output) = Utils.RunShellCommand(command, envVarsDict, workingDir: Environment.CurrentDirectory); + + if (exitCode != 0) + { + Log.LogError($"Failed to compile {srcFile} -> {objFile}: {output}"); + return false; + } + + ITaskItem newItem = new TaskItem(objFile); + newItem.SetMetadata("SourceFile", srcFile); + outputItems.Add(newItem); + + return true; + } + catch (Exception ex) + { + Log.LogError($"Failed to compile {srcFile} -> {objFile}: {ex.Message}"); + return false; + } + } + } + + private IDictionary GetEnvironmentVariablesDict() + { + Dictionary envVarsDict = new(); + if (EnvironmentVariables == null) + return envVarsDict; + + foreach (var item in EnvironmentVariables) + { + var parts = item.ItemSpec.Split(new char[] {'='}, 2, StringSplitOptions.None); + if (parts.Length == 0) + continue; + + string key = parts[0]; + string value = parts.Length > 1 ? parts[1] : string.Empty; + + envVarsDict[key] = value; + } + + return envVarsDict; + } + } +} diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs index 660272e268aa4..46c0174148be2 100644 --- a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs +++ b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs @@ -3,16 +3,11 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; -using System.Text; -using System.Text.Encodings.Web; using System.Text.Json; using System.Text.Json.Serialization; -using System.Reflection; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj b/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj index 23074621b8973..b27176e277400 100644 --- a/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj +++ b/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj @@ -14,6 +14,8 @@ + + From 89603ecdd466f8a01f5ef59d9a4d2f2c13813e89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Rylek?= Date: Wed, 23 Jun 2021 23:24:06 +0200 Subject: [PATCH 086/107] Move setting fHasVirtualStaticMethods out of sanity check section (#54574) --- src/coreclr/vm/methodtablebuilder.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index 24dfe4c124015..ad827323c69bc 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -2906,6 +2906,13 @@ MethodTableBuilder::EnumerateClassMethods() IDS_CLASSLOAD_BADSPECIALMETHOD, tok); } + + // Check for the presence of virtual static methods + if (IsMdVirtual(dwMemberAttrs) && IsMdStatic(dwMemberAttrs)) + { + bmtProp->fHasVirtualStaticMethods = TRUE; + } + // // But first - minimal flags validity checks // @@ -2972,11 +2979,7 @@ MethodTableBuilder::EnumerateClassMethods() } if(IsMdStatic(dwMemberAttrs)) { - if (fIsClassInterface) - { - bmtProp->fHasVirtualStaticMethods = TRUE; - } - else + if (!fIsClassInterface) { // Static virtual methods are only allowed to exist in interfaces BuildMethodTableThrowException(BFA_VIRTUAL_STATIC_METHOD); From 707cf4fee594c19ed0dbf78349116f3e99bbbb55 Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Thu, 24 Jun 2021 00:37:17 +0300 Subject: [PATCH 087/107] Move System.Object serialization to ObjectConverter (#54436) * move System.Object serialization to ObjectConverter * simply customized object converter test --- .../Converters/Value/ObjectConverter.cs | 5 ++- .../Json/Serialization/JsonConverterOfT.cs | 32 ++++++++++------- .../Metadata/JsonPropertyInfoOfT.cs | 2 +- .../CustomConverterTests.Object.cs | 22 +++++++++++- .../Serialization/OptionsTests.cs | 35 +++++++------------ 5 files changed, 58 insertions(+), 38 deletions(-) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/ObjectConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/ObjectConverter.cs index 597dce6b7ef17..65b47d6507431 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/ObjectConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/ObjectConverter.cs @@ -24,7 +24,10 @@ public ObjectConverter() public override void Write(Utf8JsonWriter writer, object? value, JsonSerializerOptions options) { - throw new InvalidOperationException(); + Debug.Assert(value?.GetType() == typeof(object)); + + writer.WriteStartObject(); + writer.WriteEndObject(); } internal override object ReadWithQuotes(ref Utf8JsonReader reader) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs index 543fbe2f5229a..33f45fd3ceda5 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs @@ -18,12 +18,11 @@ public abstract partial class JsonConverter : JsonConverter /// protected internal JsonConverter() { - // Today only typeof(object) can have polymorphic writes. - // In the future, this will be check for !IsSealed (and excluding value types). - CanBePolymorphic = TypeToConvert == JsonTypeInfo.ObjectType; + IsInternalConverter = GetType().Assembly == typeof(JsonConverter).Assembly; + // Today only the internal JsonConverter can have polymorphic writes. + CanBePolymorphic = IsInternalConverter && TypeToConvert == JsonTypeInfo.ObjectType; IsValueType = TypeToConvert.IsValueType; CanBeNull = default(T) is null; - IsInternalConverter = GetType().Assembly == typeof(JsonConverter).Assembly; if (HandleNull) { @@ -332,7 +331,7 @@ internal bool TryWrite(Utf8JsonWriter writer, in T value, JsonSerializerOptions bool ignoreCyclesPopReference = false; if ( -#if NET6_0_OR_GREATER +#if NET5_0_OR_GREATER !typeof(T).IsValueType && // treated as a constant by recent versions of the JIT. #else !IsValueType && @@ -368,15 +367,11 @@ internal bool TryWrite(Utf8JsonWriter writer, in T value, JsonSerializerOptions if (CanBePolymorphic) { + Debug.Assert(IsInternalConverter); + Type type = value.GetType(); - if (type == JsonTypeInfo.ObjectType) - { - writer.WriteStartObject(); - writer.WriteEndObject(); - return true; - } - if (type != TypeToConvert && IsInternalConverter) + if (type != TypeToConvert) { // For internal converter only: Handle polymorphic case and get the new converter. // Custom converter, even though polymorphic converter, get called for reading AND writing. @@ -420,6 +415,19 @@ internal bool TryWrite(Utf8JsonWriter writer, in T value, JsonSerializerOptions } VerifyWrite(originalPropertyDepth, writer); + + if ( +#if NET5_0_OR_GREATER + !typeof(T).IsValueType && // treated as a constant by recent versions of the JIT. +#endif + ignoreCyclesPopReference) + { + // should only be entered if we're serializing instances + // of type object using the internal object converter. + Debug.Assert(value?.GetType() == typeof(object) && IsInternalConverter); + state.ReferenceResolver.PopReferenceForCycleDetection(); + } + return true; } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoOfT.cs index 0f2ff8574f08d..8e268c2509940 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoOfT.cs @@ -235,7 +235,7 @@ internal override bool GetMemberAndWriteJson(object obj, ref WriteStack state, U T value = Get!(obj); if ( -#if NET6_0_OR_GREATER +#if NET5_0_OR_GREATER !typeof(T).IsValueType && // treated as a constant by recent versions of the JIT. #else !Converter.IsValueType && diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CustomConverterTests/CustomConverterTests.Object.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CustomConverterTests/CustomConverterTests.Object.cs index 0ab7ad020de5d..36309616eaf5f 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CustomConverterTests/CustomConverterTests.Object.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CustomConverterTests/CustomConverterTests.Object.cs @@ -307,7 +307,9 @@ public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonS public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options) { - throw new InvalidOperationException("Should not get here."); + Assert.IsType(value); + writer.WriteStartObject(); + writer.WriteEndObject(); } } @@ -732,5 +734,23 @@ static void Verify(JsonSerializerOptions options) options.Converters.Add(new SystemObjectNewtonsoftCompatibleConverter()); Verify(options); } + + [Fact] + public static void CanCustomizeSystemObjectSerialization() + { + var options = new JsonSerializerOptions { Converters = { new CustomSystemObjectConverter() } }; + + string expectedJson = "42"; + string actualJson = JsonSerializer.Serialize(new object(), options); + Assert.Equal(expectedJson, actualJson); + } + + private class CustomSystemObjectConverter : JsonConverter + { + public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => throw new NotImplementedException(); + public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options) + => writer.WriteNumberValue(42); + } } + } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/OptionsTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/OptionsTests.cs index 06747c8778626..f7d80c2a01e63 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/OptionsTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/OptionsTests.cs @@ -328,12 +328,12 @@ public static void JsonEncodedTextStringsCustomAllowAll(string message, string e [Fact] public static void Options_GetConverterForObjectJsonElement_GivesCorrectConverter() { - GenericObjectOrJsonElementConverterTestHelper("ObjectConverter", new object(), "[3]", true); + GenericObjectOrJsonElementConverterTestHelper("ObjectConverter", new object(), "{}"); JsonElement element = JsonDocument.Parse("[3]").RootElement; - GenericObjectOrJsonElementConverterTestHelper("JsonElementConverter", element, "[3]", false); + GenericObjectOrJsonElementConverterTestHelper("JsonElementConverter", element, "[3]"); } - private static void GenericObjectOrJsonElementConverterTestHelper(string converterName, object objectValue, string stringValue, bool throws) + private static void GenericObjectOrJsonElementConverterTestHelper(string converterName, object objectValue, string stringValue) { var options = new JsonSerializerOptions(); @@ -347,10 +347,7 @@ private static void GenericObjectOrJsonElementConverterTestHelper(string conv if (readValue is JsonElement element) { - Assert.Equal(JsonValueKind.Array, element.ValueKind); - JsonElement.ArrayEnumerator iterator = element.EnumerateArray(); - Assert.True(iterator.MoveNext()); - Assert.Equal(3, iterator.Current.GetInt32()); + JsonTestHelper.AssertJsonEqual(stringValue, element.ToString()); } else { @@ -360,22 +357,14 @@ private static void GenericObjectOrJsonElementConverterTestHelper(string conv using (var stream = new MemoryStream()) using (var writer = new Utf8JsonWriter(stream)) { - if (throws) - { - Assert.Throws(() => converter.Write(writer, (T)objectValue, options)); - Assert.Throws(() => converter.Write(writer, (T)objectValue, null)); - } - else - { - converter.Write(writer, (T)objectValue, options); - writer.Flush(); - Assert.Equal(stringValue, Encoding.UTF8.GetString(stream.ToArray())); - - writer.Reset(stream); - converter.Write(writer, (T)objectValue, null); // Test with null option - writer.Flush(); - Assert.Equal(stringValue + stringValue, Encoding.UTF8.GetString(stream.ToArray())); - } + converter.Write(writer, (T)objectValue, options); + writer.Flush(); + Assert.Equal(stringValue, Encoding.UTF8.GetString(stream.ToArray())); + + writer.Reset(stream); + converter.Write(writer, (T)objectValue, null); // Test with null option + writer.Flush(); + Assert.Equal(stringValue + stringValue, Encoding.UTF8.GetString(stream.ToArray())); } } From d7a5b899807329db2cf2d4afeef0aaa8edca055c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Rylek?= Date: Wed, 23 Jun 2021 23:39:28 +0200 Subject: [PATCH 088/107] Put Crossgen2 in sync with #54235 (#54438) --- .../Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs | 5 ++--- .../tools/Common/TypeSystem/Interop/IL/MarshalUtils.cs | 4 ++-- .../MarshalUtilsTests.cs | 4 ++-- src/tests/issues.targets | 3 --- 4 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs b/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs index 2bf40935a38b6..4109bb701aa9a 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs @@ -782,10 +782,9 @@ public LayoutInt CalculateFieldBaseOffset(MetadataType type, bool requiresAlign8 if (!type.IsValueType && type.HasBaseType) { cumulativeInstanceFieldPos = type.BaseType.InstanceByteCountUnaligned; - if (!type.BaseType.InstanceByteCountUnaligned.IsIndeterminate) + if (!cumulativeInstanceFieldPos.IsIndeterminate) { - cumulativeInstanceFieldPos = type.BaseType.InstanceByteCountUnaligned; - if (type.BaseType.IsZeroSizedReferenceType && ((MetadataType)type.BaseType).HasLayout()) + if (requiresAlignedBase && type.BaseType.IsZeroSizedReferenceType && ((MetadataType)type.BaseType).HasLayout()) { cumulativeInstanceFieldPos += LayoutInt.One; } diff --git a/src/coreclr/tools/Common/TypeSystem/Interop/IL/MarshalUtils.cs b/src/coreclr/tools/Common/TypeSystem/Interop/IL/MarshalUtils.cs index 84dcc80be1323..33c609ee23b80 100644 --- a/src/coreclr/tools/Common/TypeSystem/Interop/IL/MarshalUtils.cs +++ b/src/coreclr/tools/Common/TypeSystem/Interop/IL/MarshalUtils.cs @@ -23,8 +23,8 @@ public static bool IsBlittableType(TypeDesc type) && !baseType.IsWellKnownType(WellKnownType.Object) && !baseType.IsWellKnownType(WellKnownType.ValueType); - // Type is blittable only if parent is also blittable and is not empty. - if (hasNonTrivialParent && (!IsBlittableType(baseType) || baseType.IsZeroSizedReferenceType)) + // Type is blittable only if parent is also blittable. + if (hasNonTrivialParent && !IsBlittableType(baseType)) { return false; } diff --git a/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/MarshalUtilsTests.cs b/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/MarshalUtilsTests.cs index c3befa87af72d..3523f5d62f8e1 100644 --- a/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/MarshalUtilsTests.cs +++ b/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/MarshalUtilsTests.cs @@ -74,10 +74,10 @@ public void IsBlittableType_TypeWithBlittableBase_ReturnsTrue(string className) [InlineData("ClassWithExplicitEmptyBase")] [InlineData("ClassWithExplicitEmptySizeZeroBase")] [InlineData("ClassWithSequentialEmptyBase")] - public void IsBlittableType_TypeWithEmptyBase_ReturnsFalse(string className) + public void IsBlittableType_TypeWithEmptyBase_ReturnsTrue(string className) { TypeDesc classWithEmptyBase = _testModule.GetType("Marshalling", className); - Assert.False(MarshalUtils.IsBlittableType(classWithEmptyBase)); + Assert.True(MarshalUtils.IsBlittableType(classWithEmptyBase)); } } } diff --git a/src/tests/issues.targets b/src/tests/issues.targets index e34d49e145543..06867ff7ee8a1 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -123,9 +123,6 @@ https://github.com/dotnet/runtime/issues/48727 - - https://github.com/dotnet/runtime/issues/54316 - From 4f4f0dbd5e674f4d19d8d51fd6717b9316224852 Mon Sep 17 00:00:00 2001 From: Thays Grazia Date: Wed, 23 Jun 2021 20:06:58 -0300 Subject: [PATCH 089/107] [wasm][debugger] Reuse debugger-agent on wasm debugger (#52300) * Trying to reuse debugger-agent on wasm debugger. This will remove a lot of code that does the same thing on mini-wasm-debugger. * Replace remove_breakpoint and clear_all_breakpoints with the ones on debugger-agent and remove unused code. * Stepping and callstack using debugger-agent. * Remove more code. * Get frame values using debugger-agent. * Working valuetypes and call function on valuetypes. make -C src/mono/wasm/ run-debugger-tests TEST_FILTER=DebuggerTests.SteppingTests.InspectValueTypeMethodArgsWhileStepping is working without use_cfo. * Failed: 316, Passed: 175 * Failed: 307, Passed: 184, Skipped: 0, Total: 491 * Failed: 280, Passed: 211 * Failed: 277, Passed: 214 * Implemented boxed value. Failed: 271, Passed: 220 * Implementing get properties on objects. Implementing handling error on debugger-agent. * Implementing callfunctionon object. Failed: 248, Passed: 243 * Implementing get pointer values. Failed: 243, Passed: 248 * Fixing pointer values and implement call on function with pointers. Failed: 226, Passed: 265 * Reimplement call function on, and implement set values. Failed: 192, Passed: 299 * Failed: 192, Passed: 299 * Fixing valuetype with null values. Failed: 184, Passed: 307 * Implemented Evaluate expressions, conditional breakpoints, all breakpoints tests are passing. Failed: 172, Passed: 319 * Fixing evaluate with value type. Failed: 156, Passed: 335 * Trim part and add cache. Failed: 148, Passed: 343 * Fixing evaluate expression. Failed: 99, Passed: 392 * GetPropertiesTests working. Failed: 53, Passed: 438 * Passing delegate tests. Failed: 31, Passed: 460 * Removing unused code. * Implementing exception handler. Failed: 30, Passed: 461 * Fixing cfo returning array. Removing more code. Removing 2 tests that tests functions which does not exist anymore. Failed: 18, Passed: 471 * Fix CallFunctionOn returning primitive types and null. Failed: 9, Passed: 480 * Failed: 7, Passed: 482 * Fixing some tests. Failed: 2, Passed: 488 * Removing a lot of code. Failed: 4, Passed: 485 * 0 ERRORS! * Removing more code. No errors. * Fixing added tests. * Return javascript callstack after managed callstack. Step out from managed code return to native wasm or javascript. Adding debug info to Wasm.Browser.Sample to help testing debugger with sample. * Change what Ankit suggested. Clear cache with valuetypes and pointers after resume or step. * Fixing suggestions. * Fix error on wasm build. * Apply suggestions from code review Co-authored-by: Larry Ewing * Changing what was suggested by @lewing. * Fix pointer tests. * Refactoring CreateJObjectForVariableValue * Changing what @lewing suggested. * Apply suggestions from code review Co-authored-by: Larry Ewing * Update src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs Co-authored-by: Larry Ewing * Update src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs Co-authored-by: Larry Ewing * Fixing @lewing changes. * Trying to fix CI. Co-authored-by: Larry Ewing --- src/mono/mono/mini/debugger-agent.c | 479 +++-- src/mono/mono/mini/debugger-agent.h | 66 + src/mono/mono/mini/debugger-engine.c | 7 - src/mono/mono/mini/debugger-engine.h | 13 +- src/mono/mono/mini/debugger-protocol.c | 1 - src/mono/mono/mini/debugger-protocol.h | 14 +- src/mono/mono/mini/mini-wasm-debugger.c | 1910 ++--------------- .../wasm/browser/Wasm.Browser.Sample.csproj | 4 + .../debugger/BrowserDebugProxy/DebugStore.cs | 3 +- .../BrowserDebugProxy/DevToolsHelper.cs | 42 +- .../MemberReferenceResolver.cs | 111 +- .../debugger/BrowserDebugProxy/MonoProxy.cs | 537 +++-- .../BrowserDebugProxy/MonoSDBHelper.cs | 1779 +++++++++++++++ .../DebuggerTestSuite/AssignmentTests.cs | 2 +- .../DebuggerTestSuite/DebuggerTestBase.cs | 1 - .../debugger/DebuggerTestSuite/MonoJsTests.cs | 79 - .../wasm/debugger/DebuggerTestSuite/Tests.cs | 39 +- src/mono/wasm/runtime/library_mono.js | 1199 +---------- 18 files changed, 2839 insertions(+), 3447 deletions(-) create mode 100644 src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs diff --git a/src/mono/mono/mini/debugger-agent.c b/src/mono/mono/mini/debugger-agent.c index eb0e2a248dc33..b1efc0d246c71 100644 --- a/src/mono/mono/mini/debugger-agent.c +++ b/src/mono/mono/mini/debugger-agent.c @@ -109,8 +109,7 @@ #define DISABLE_SOCKET_TRANSPORT #endif -#ifndef DISABLE_SDB - +#if !defined (DISABLE_SDB) || defined(TARGET_WASM) #include #include @@ -145,28 +144,6 @@ typedef struct { gboolean using_icordbg; } AgentConfig; -typedef struct _InvokeData InvokeData; - -struct _InvokeData -{ - int id; - int flags; - guint8 *p; - guint8 *endp; - /* This is the context which needs to be restored after the invoke */ - MonoContext ctx; - gboolean has_ctx; - /* - * If this is set, invoke this method with the arguments given by ARGS. - */ - MonoMethod *method; - gpointer *args; - guint32 suspend_count; - int nmethods; - - InvokeData *last_invoke; -}; - struct _DebuggerTlsData { MonoThreadUnwindState context; @@ -262,15 +239,6 @@ struct _DebuggerTlsData { gboolean gc_finalizing; }; -typedef struct { - const char *name; - void (*connect) (const char *address); - void (*close1) (void); - void (*close2) (void); - gboolean (*send) (void *buf, int len); - int (*recv) (void *buf, int len); -} DebuggerTransport; - /* Buffered reply packets */ static ReplyPacket reply_packets [128]; static int nreply_packets; @@ -314,7 +282,9 @@ typedef struct { /* * Globals */ - +#ifdef TARGET_WASM +static DebuggerTlsData debugger_wasm_thread; +#endif static AgentConfig agent_config; /* @@ -397,6 +367,28 @@ static gint32 suspend_count; /* Whenever to buffer reply messages and send them together */ static gboolean buffer_replies; + +#ifndef TARGET_WASM +#define GET_TLS_DATA_FROM_THREAD(thread) \ + DebuggerTlsData *tls = NULL; \ + mono_loader_lock(); \ + if (thread_to_tls != NULL) \ + tls = (DebuggerTlsData*)mono_g_hash_table_lookup(thread_to_tls, thread); \ + mono_loader_unlock(); +#define GET_DEBUGGER_TLS() \ + DebuggerTlsData *tls; \ + tls = (DebuggerTlsData *)mono_native_tls_get_value (debugger_tls_id); +#else +#define GET_TLS_DATA_FROM_THREAD(thread) \ + DebuggerTlsData *tls; \ + tls = &debugger_wasm_thread; +#define GET_DEBUGGER_TLS() \ + DebuggerTlsData *tls; \ + tls = &debugger_wasm_thread; +#endif + +//mono_native_tls_get_value (debugger_tls_id); + #define dbg_lock mono_de_lock #define dbg_unlock mono_de_unlock @@ -460,6 +452,7 @@ static void objrefs_init (void); static void objrefs_cleanup (void); static void ids_init (void); + static void ids_cleanup (void); static void suspend_init (void); @@ -478,19 +471,13 @@ static MonoContext* tls_get_restore_state (void *the_tls); static gboolean try_process_suspend (void *tls, MonoContext *ctx, gboolean from_breakpoint); static gboolean begin_breakpoint_processing (void *tls, MonoContext *ctx, MonoJitInfo *ji, gboolean from_signal); static void begin_single_step_processing (MonoContext *ctx, gboolean from_signal); -static void ss_discard_frame_context (void *the_tls); -static void ss_calculate_framecount (void *tls, MonoContext *ctx, gboolean force_use_ctx, DbgEngineStackFrame ***frames, int *nframes); static gboolean ensure_jit (DbgEngineStackFrame* the_frame); static int ensure_runtime_is_suspended (void); -static int get_this_async_id (DbgEngineStackFrame *frame); -static void* create_breakpoint_events (GPtrArray *ss_reqs, GPtrArray *bp_reqs, MonoJitInfo *ji, EventKind kind); -static void process_breakpoint_events (void *_evts, MonoMethod *method, MonoContext *ctx, int il_offset); -static int ss_create_init_args (SingleStepReq *ss_req, SingleStepArgs *args); -static void ss_args_destroy (SingleStepArgs *ss_args); static int handle_multiple_ss_requests (void); static GENERATE_TRY_GET_CLASS_WITH_CACHE (fixed_buffer, "System.Runtime.CompilerServices", "FixedBufferAttribute") + #ifndef DISABLE_SOCKET_TRANSPORT static void register_socket_transport (void); @@ -710,17 +697,17 @@ debugger_agent_init (void) cbs.try_process_suspend = try_process_suspend; cbs.begin_breakpoint_processing = begin_breakpoint_processing; cbs.begin_single_step_processing = begin_single_step_processing; - cbs.ss_discard_frame_context = ss_discard_frame_context; - cbs.ss_calculate_framecount = ss_calculate_framecount; + cbs.ss_discard_frame_context = mono_ss_discard_frame_context; + cbs.ss_calculate_framecount = mono_ss_calculate_framecount; cbs.ensure_jit = ensure_jit; cbs.ensure_runtime_is_suspended = ensure_runtime_is_suspended; - cbs.get_this_async_id = get_this_async_id; + cbs.get_this_async_id = mono_get_this_async_id; cbs.set_set_notification_for_wait_completion_flag = set_set_notification_for_wait_completion_flag; cbs.get_notify_debugger_of_wait_completion_method = get_notify_debugger_of_wait_completion_method; - cbs.create_breakpoint_events = create_breakpoint_events; - cbs.process_breakpoint_events = process_breakpoint_events; - cbs.ss_create_init_args = ss_create_init_args; - cbs.ss_args_destroy = ss_args_destroy; + cbs.create_breakpoint_events = mono_dbg_create_breakpoint_events; + cbs.process_breakpoint_events = mono_dbg_process_breakpoint_events; + cbs.ss_create_init_args = mono_ss_create_init_args; + cbs.ss_args_destroy = mono_ss_args_destroy; cbs.handle_multiple_ss_requests = handle_multiple_ss_requests; mono_de_init (&cbs); @@ -1287,9 +1274,6 @@ static DebuggerTransport *transport; static DebuggerTransport transports [MAX_TRANSPORTS]; static int ntransports; -MONO_API void -mono_debugger_agent_register_transport (DebuggerTransport *trans); - void mono_debugger_agent_register_transport (DebuggerTransport *trans) { @@ -1590,6 +1574,24 @@ static GHashTable *obj_to_objref; /* Protected by the dbg lock */ static MonoGHashTable *suspended_objs; +#ifdef TARGET_WASM +void mono_init_debugger_agent_for_wasm (int log_level_parm) +{ + if (mono_atomic_cas_i32 (&agent_inited, 1, 0) == 1) + return; + + ids_init(); + objrefs = g_hash_table_new_full (NULL, NULL, NULL, mono_debugger_free_objref); + obj_to_objref = g_hash_table_new (NULL, NULL); + + log_level = log_level; + event_requests = g_ptr_array_new (); + vm_start_event_sent = TRUE; + transport = &transports [0]; + memset(&debugger_wasm_thread, 0, sizeof(DebuggerTlsData)); + agent_config.enabled = TRUE; +} +#endif static void @@ -1986,7 +1988,6 @@ static int buffer_add_ptr_id (Buffer *buf, MonoDomain *domain, IdType type, gpointer val) { int id = get_id (domain, type, val); - buffer_add_id (buf, id); return id; } @@ -2176,6 +2177,21 @@ save_thread_context (MonoContext *ctx) mono_thread_state_init_from_current (&tls->context); } +#ifdef TARGET_WASM +void +mono_wasm_save_thread_context (void) +{ + debugger_wasm_thread.really_suspended = TRUE; + mono_thread_state_init_from_current (&debugger_wasm_thread.context); +} + +DebuggerTlsData* +mono_wasm_get_tls (void) +{ + return &debugger_wasm_thread; +} +#endif + static MonoCoopMutex suspend_mutex; /* Cond variable used to wait for suspend_count becoming 0 */ @@ -2700,7 +2716,8 @@ static int count_threads_to_wait_for (void) { int count = 0; - + if (thread_to_tls == NULL) + return 0; mono_loader_lock (); mono_g_hash_table_foreach (thread_to_tls, count_thread, &count); mono_loader_unlock (); @@ -3049,7 +3066,7 @@ compute_frame_info (MonoInternalThread *thread, DebuggerTlsData *tls, gboolean f tls->frames = new_frames; tls->frame_count = new_frame_count; tls->frames_up_to_date = TRUE; - +#ifndef TARGET_WASM if (CHECK_PROTOCOL_VERSION (2, 52)) { MonoJitTlsData *jit_data = thread->thread_info->jit_data; gboolean has_interp_resume_state = FALSE; @@ -3064,6 +3081,7 @@ compute_frame_info (MonoInternalThread *thread, DebuggerTlsData *tls, gboolean f } } } +#endif } /* @@ -3494,7 +3512,7 @@ process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx return; } } - + if (event == EVENT_KIND_VM_START) suspend_policy = agent_config.suspend ? SUSPEND_POLICY_ALL : SUSPEND_POLICY_NONE; @@ -3552,12 +3570,10 @@ process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx break; case EVENT_KIND_BREAKPOINT: case EVENT_KIND_STEP: { - DebuggerTlsData *tls; - tls = (DebuggerTlsData *)mono_native_tls_get_value (debugger_tls_id); + GET_DEBUGGER_TLS(); g_assert (tls); mono_stopwatch_stop (&tls->step_time); MonoMethod *method = (MonoMethod *)arg; - buffer_add_methodid (&buf, domain, method); buffer_add_long (&buf, il_offset); break; @@ -3578,6 +3594,9 @@ process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx case EVENT_KIND_EXCEPTION: { EventInfo *ei = (EventInfo *)arg; buffer_add_objid (&buf, ei->exc); +#ifdef TARGET_WASM + buffer_add_byte (&buf, ei->caught); +#endif /* * We are not yet suspending, so get_objref () will not keep this object alive. So we need to do it * later after the suspension. (#12494). @@ -3586,8 +3605,7 @@ process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx break; } case EVENT_KIND_USER_BREAK: { - DebuggerTlsData *tls; - tls = (DebuggerTlsData *)mono_native_tls_get_value (debugger_tls_id); + GET_DEBUGGER_TLS(); g_assert (tls); // We are already processing a breakpoint event if (tls->disable_breakpoints) @@ -4042,14 +4060,18 @@ event_requests_cleanup (void) * * Ensure DebuggerTlsData fields are filled out. */ -static void -ss_calculate_framecount (void *the_tls, MonoContext *ctx, gboolean force_use_ctx, DbgEngineStackFrame ***frames, int *nframes) +void +mono_ss_calculate_framecount (void *the_tls, MonoContext *ctx, gboolean force_use_ctx, DbgEngineStackFrame ***frames, int *nframes) { DebuggerTlsData *tls = (DebuggerTlsData*)the_tls; - +#ifndef TARGET_WASM if (force_use_ctx || !tls->context.valid) mono_thread_state_init_from_monoctx (&tls->context, ctx); compute_frame_info (tls->thread, tls, FALSE); +#else + compute_frame_info (tls->thread, tls, TRUE); +#endif + if (frames) *frames = (DbgEngineStackFrame**)tls->frames; if (nframes) @@ -4061,8 +4083,8 @@ ss_calculate_framecount (void *the_tls, MonoContext *ctx, gboolean force_use_ctx * * Discard frame data and invalidate any context */ -static void -ss_discard_frame_context (void *the_tls) +void +mono_ss_discard_frame_context (void *the_tls) { DebuggerTlsData *tls = (DebuggerTlsData*)the_tls; tls->context.valid = FALSE; @@ -4107,8 +4129,8 @@ breakpoint_matches_assembly (MonoBreakpoint *bp, MonoAssembly *assembly) //This ID is used to figure out if breakpoint hit on resumeOffset belongs to us or not //since thread probably changed... -static int -get_this_async_id (DbgEngineStackFrame *frame) +int +mono_get_this_async_id (DbgEngineStackFrame *frame) { MonoClassField *builder_field; gpointer builder; @@ -4161,8 +4183,11 @@ begin_breakpoint_processing (void *the_tls, MonoContext *ctx, MonoJitInfo *ji, g * Skip the instruction causing the breakpoint signal. */ if (from_signal) +#ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED mono_arch_skip_breakpoint (ctx, ji); - +#else + NOT_IMPLEMENTED; +#endif if (tls->disable_breakpoints) return FALSE; return TRUE; @@ -4174,8 +4199,8 @@ typedef struct { int suspend_policy; } BreakPointEvents; -static void* -create_breakpoint_events (GPtrArray *ss_reqs, GPtrArray *bp_reqs, MonoJitInfo *ji, EventKind kind) +void* +mono_dbg_create_breakpoint_events (GPtrArray *ss_reqs, GPtrArray *bp_reqs, MonoJitInfo *ji, EventKind kind) { int suspend_policy = 0; BreakPointEvents *evts = g_new0 (BreakPointEvents, 1); @@ -4191,8 +4216,8 @@ create_breakpoint_events (GPtrArray *ss_reqs, GPtrArray *bp_reqs, MonoJitInfo *j return evts; } -static void -process_breakpoint_events (void *_evts, MonoMethod *method, MonoContext *ctx, int il_offset) +void +mono_dbg_process_breakpoint_events (void *_evts, MonoMethod *method, MonoContext *ctx, int il_offset) { BreakPointEvents *evts = (BreakPointEvents*)_evts; /* @@ -4301,8 +4326,8 @@ user_break_cb (StackFrameInfo *frame, MonoContext *ctx, gpointer user_data) /* * Called by System.Diagnostics.Debugger:Break (). */ -static void -debugger_agent_user_break (void) +void +mono_dbg_debugger_agent_user_break (void) { if (agent_config.enabled) { MonoContext ctx; @@ -4332,7 +4357,11 @@ static void begin_single_step_processing (MonoContext *ctx, gboolean from_signal) { if (from_signal) +#ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED mono_arch_skip_single_step (ctx); +#else + NOT_IMPLEMENTED; +#endif } static void @@ -4364,7 +4393,11 @@ debugger_agent_single_step_event (void *sigctx) MonoContext ctx; mono_sigctx_to_monoctx (sigctx, &ctx); +#ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED mono_arch_skip_single_step (&ctx); +#else + NOT_IMPLEMENTED; +#endif mono_monoctx_to_sigctx (&ctx, sigctx); return; } @@ -4444,8 +4477,8 @@ debugger_agent_breakpoint_from_context (MonoContext *ctx) if (MONO_CONTEXT_GET_IP (ctx) == orig_ip - 1) MONO_CONTEXT_SET_IP (ctx, orig_ip); } -static void -ss_args_destroy (SingleStepArgs *ss_args) +void +mono_ss_args_destroy (SingleStepArgs *ss_args) { if (ss_args->frames) free_frames ((StackFrame**)ss_args->frames, ss_args->nframes); @@ -4470,8 +4503,8 @@ ensure_runtime_is_suspended (void) return ERR_NONE; } -static int -ss_create_init_args (SingleStepReq *ss_req, SingleStepArgs *args) +int +mono_ss_create_init_args (SingleStepReq *ss_req, SingleStepArgs *args) { MonoSeqPointInfo *info = NULL; gboolean found_sp; @@ -4481,10 +4514,9 @@ ss_create_init_args (SingleStepReq *ss_req, SingleStepArgs *args) gboolean set_ip = FALSE; StackFrame **frames = NULL; int nframes = 0; - - mono_loader_lock (); - DebuggerTlsData *tls = (DebuggerTlsData *)mono_g_hash_table_lookup (thread_to_tls, ss_req->thread); - mono_loader_unlock (); + + GET_TLS_DATA_FROM_THREAD (ss_req->thread); + g_assert (tls); if (!tls->context.valid) { PRINT_DEBUG_MSG (1, "Received a single step request on a thread with no managed frames.\n"); @@ -4727,8 +4759,8 @@ debugger_agent_unhandled_exception (MonoException *exc) process_event (EVENT_KIND_EXCEPTION, &ei, 0, NULL, events, suspend_policy); } -static void -debugger_agent_handle_exception (MonoException *exc, MonoContext *throw_ctx, +void +mono_debugger_agent_handle_exception (MonoException *exc, MonoContext *throw_ctx, MonoContext *catch_ctx, StackFrameInfo *catch_frame) { if (catch_ctx == NULL && catch_frame == NULL && mini_debug_options.suspend_on_unhandled && mono_object_class (exc) != mono_defaults.threadabortexception_class) { @@ -4736,23 +4768,15 @@ debugger_agent_handle_exception (MonoException *exc, MonoContext *throw_ctx, while (1) ; } - int i, j, suspend_policy; GSList *events; MonoJitInfo *ji, *catch_ji; EventInfo ei; - DebuggerTlsData *tls = NULL; - - if (thread_to_tls != NULL) { - MonoInternalThread *thread = mono_thread_internal_current (); - - mono_loader_lock (); - tls = (DebuggerTlsData *)mono_g_hash_table_lookup (thread_to_tls, thread); - mono_loader_unlock (); - - if (tls && tls->abort_requested) + GET_TLS_DATA_FROM_THREAD (mono_thread_internal_current ()); + if (tls != NULL) { + if (tls->abort_requested) return; - if (tls && tls->disable_breakpoints) + if (tls->disable_breakpoints) return; } @@ -4965,7 +4989,10 @@ buffer_add_info_for_null_value (Buffer* buf, MonoType* t, MonoDomain* domain) buffer_add_int (buf, m_class_get_rank (mono_class_from_mono_type_internal (t))); if (m_class_get_byval_arg (m_class_get_element_class (mono_class_from_mono_type_internal (t)))->type == MONO_TYPE_CLASS) buffer_add_typeid (buf, domain, m_class_get_element_class (mono_class_from_mono_type_internal (t))); + buffer_add_typeid (buf, domain, mono_class_from_mono_type_internal (t)); break; + default: + buffer_add_typeid (buf, domain, mono_class_from_mono_type_internal (t)); } } /* @@ -5149,6 +5176,9 @@ buffer_add_value_full (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain, buffer_add_byte (buf, MONO_TYPE_VALUETYPE); buffer_add_byte (buf, m_class_is_enumtype (klass)); + + if (CHECK_PROTOCOL_VERSION(2, 61)) + buffer_add_byte(buf, boxed_vtype); buffer_add_typeid (buf, domain, klass); nfields = 0; @@ -5222,9 +5252,8 @@ decode_vtype (MonoType *t, MonoDomain *domain, gpointer void_addr, gpointer void ErrorCode err; is_enum = decode_byte (buf, &buf, limit); - /* Enums are sent as a normal vtype */ - if (is_enum) - return ERR_NOT_IMPLEMENTED; + if (CHECK_PROTOCOL_VERSION(2, 61)) + decode_byte (buf, &buf, limit); klass = decode_typeid (buf, &buf, limit, &d, &err); if (err != ERR_NONE) return err; @@ -5413,7 +5442,7 @@ decode_value_internal (MonoType *t, int type, MonoDomain *domain, guint8 *addr, handle_ref: default: if (MONO_TYPE_IS_REFERENCE (t)) { - if (type == MONO_TYPE_OBJECT || type == MONO_TYPE_STRING) { + if (type == MONO_TYPE_CLASS || type == MONO_TYPE_OBJECT || type == MONO_TYPE_STRING) { int objid = decode_objid (buf, &buf, limit); ErrorCode err; MonoObject *obj; @@ -5435,7 +5464,12 @@ decode_value_internal (MonoType *t, int type, MonoDomain *domain, guint8 *addr, mono_gc_wbarrier_generic_store_internal (addr, obj); } else if (type == VALUE_TYPE_ID_NULL) { + if (CHECK_PROTOCOL_VERSION (2, 59)) { + decode_byte (buf, &buf, limit); + decode_int (buf, &buf, limit); //not used + } *(MonoObject**)addr = NULL; + } else if (type == MONO_TYPE_VALUETYPE) { ERROR_DECL (error); guint8 *buf2; @@ -5452,8 +5486,7 @@ decode_value_internal (MonoType *t, int type, MonoDomain *domain, guint8 *addr, */ buf2 = buf; is_enum = decode_byte (buf, &buf, limit); - if (is_enum) - return ERR_NOT_IMPLEMENTED; + decode_byte (buf, &buf, limit); //ignore is boxed klass = decode_typeid (buf, &buf, limit, &d, &err); if (err != ERR_NONE) return err; @@ -5913,8 +5946,8 @@ add_thread (gpointer key, gpointer value, gpointer user_data) } -static ErrorCode -do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke, guint8 *p, guint8 **endp) +ErrorCode +mono_do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke, guint8 *p, guint8 **endp) { ERROR_DECL (error); guint8 *end = invoke->endp; @@ -5981,7 +6014,7 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke, guint8 return err; } } else { - if (!(m->flags & METHOD_ATTRIBUTE_STATIC && CHECK_PROTOCOL_VERSION (2, 59))) { //on icordbg I couldn't find an object when invoking a static method maybe I can change this later + if (!(m->flags & METHOD_ATTRIBUTE_STATIC) || (m->flags & METHOD_ATTRIBUTE_STATIC && !CHECK_PROTOCOL_VERSION (2, 59))) { //on icordbg I couldn't find an object when invoking a static method maybe I can change this later err = decode_value(m_class_get_byval_arg(m->klass), domain, this_buf, p, &p, end, FALSE); if (err != ERR_NONE) return err; @@ -6236,7 +6269,7 @@ invoke_method (void) if (err) { /* Fail the other invokes as well */ } else { - err = do_invoke_method (tls, &buf, invoke, p, &p); + err = mono_do_invoke_method (tls, &buf, invoke, p, &p); } if (tls->abort_requested) { @@ -6929,6 +6962,39 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf) buffer_add_byte_array (buf, memory, size); break; } + case MDBGPROT_CMD_GET_ASSEMBLY_BY_NAME: { + int i; + char* assembly_name = decode_string (p, &p, end); + //we get 'foo.dll' but mono_assembly_load expects 'foo' so we strip the last dot + char *lookup_name = g_strdup (assembly_name); + for (i = strlen (lookup_name) - 1; i >= 0; --i) { + if (lookup_name [i] == '.') { + lookup_name [i] = 0; + break; + } + } + + //resolve the assembly + MonoImageOpenStatus status; + MonoAssemblyName* aname = mono_assembly_name_new (lookup_name); + if (!aname) { + PRINT_DEBUG_MSG (1, "Could not resolve assembly %s\n", assembly_name); + buffer_add_int(buf, -1); + break; + } + MonoAssemblyByNameRequest byname_req; + mono_assembly_request_prepare_byname (&byname_req, MONO_ASMCTX_DEFAULT, mono_alc_get_default ()); + MonoAssembly *assembly = mono_assembly_request_byname (aname, &byname_req, &status); + g_free (lookup_name); + mono_assembly_name_free_internal (aname); + if (!assembly) { + PRINT_DEBUG_MSG (1, "Could not resolve assembly %s\n", assembly_name); + buffer_add_int(buf, -1); + break; + } + buffer_add_assemblyid (buf, mono_get_root_domain (), assembly); + break; + } default: return ERR_NOT_IMPLEMENTED; } @@ -7091,10 +7157,9 @@ event_commands (int command, guint8 *p, guint8 *end, Buffer *buf) g_free (req); return err; } + + GET_TLS_DATA_FROM_THREAD (THREAD_TO_INTERNAL(step_thread)); - mono_loader_lock (); - DebuggerTlsData *tls = (DebuggerTlsData *)mono_g_hash_table_lookup (thread_to_tls, THREAD_TO_INTERNAL(step_thread)); - mono_loader_unlock (); g_assert (tls); if (tls->terminated) { @@ -7108,6 +7173,22 @@ event_commands (int command, guint8 *p, guint8 *end, Buffer *buf) g_free (req); return err; } +#ifdef TARGET_WASM + int isBPOnManagedCode = 0; + SingleStepReq *ss_req = req->info; + if (ss_req && ss_req->bps) { + GSList *l; + + for (l = ss_req->bps; l; l = l->next) { + if (((MonoBreakpoint *)l->data)->method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE) + isBPOnManagedCode = 1; + } + } + if (!isBPOnManagedCode) { + mono_de_cancel_all_ss (); + } + buffer_add_byte (buf, isBPOnManagedCode); +#endif } else if (req->event_kind == EVENT_KIND_METHOD_ENTRY) { req->info = mono_de_set_breakpoint (NULL, METHOD_ENTRY_IL_OFFSET, req, NULL); } else if (req->event_kind == EVENT_KIND_METHOD_EXIT) { @@ -7738,7 +7819,11 @@ type_commands_internal (int command, MonoClass *klass, MonoDomain *domain, guint buffer_add_string (buf, m_class_get_name_space (klass)); buffer_add_string (buf, m_class_get_name (klass)); // FIXME: byref - name = mono_type_get_name_full (m_class_get_byval_arg (klass), MONO_TYPE_NAME_FORMAT_FULL_NAME); + + MonoTypeNameFormat format = MONO_TYPE_NAME_FORMAT_FULL_NAME; + if (CHECK_PROTOCOL_VERSION(2, 61)) + format = (MonoTypeNameFormat) decode_int (p, &p, end); + name = mono_type_get_name_full (m_class_get_byval_arg (klass), format); buffer_add_string (buf, name); g_free (name); buffer_add_assemblyid (buf, domain, m_class_get_image (klass)->assembly); @@ -8182,6 +8267,23 @@ type_commands_internal (int command, MonoClass *klass, MonoDomain *domain, guint buffer_add_int (buf, value_size); break; } + case MDBGPROT_CMD_TYPE_GET_PARENTS: { + MonoClass *parent_klass = m_class_get_parent (klass); + int count = 0; + while (parent_klass != NULL) + { + count++; + parent_klass = m_class_get_parent (parent_klass); + } + buffer_add_int (buf, count); + parent_klass = m_class_get_parent (klass); + while (parent_klass != NULL) + { + buffer_add_typeid (buf, domain, parent_klass); + parent_klass = m_class_get_parent (parent_klass); + } + break; + } default: err = ERR_NOT_IMPLEMENTED; goto exit; @@ -8237,7 +8339,11 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g switch (command) { case CMD_METHOD_GET_NAME: { buffer_add_string (buf, method->name); - break; + break; + } + case MDBGPROT_CMD_METHOD_GET_NAME_FULL: { + buffer_add_string (buf, mono_method_full_name (method, FALSE)); + break; } case MDBGPROT_CMD_METHOD_GET_CLASS_TOKEN: { buffer_add_int (buf, m_class_get_type_token (method->klass)); @@ -8676,6 +8782,16 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g buffer_add_assemblyid(buf, mono_domain_get (), m_class_get_image(method->klass)->assembly); break; } + case MDBGPROT_CMD_METHOD_HAS_ASYNC_DEBUG_INFO: { + MonoDebugMethodAsyncInfo* async_method = mono_debug_lookup_method_async_debug_info (method); + if (async_method) { + buffer_add_byte(buf, TRUE); + mono_debug_free_method_async_debug_info (async_method); + } + else + buffer_add_byte(buf, FALSE); + break; + } default: return ERR_NOT_IMPLEMENTED; } @@ -8761,7 +8877,6 @@ thread_commands (int command, guint8 *p, guint8 *end, Buffer *buf) break; } case MDBGPROT_CMD_THREAD_GET_CONTEXT: { - DebuggerTlsData *tls; int start_frame; while (!is_suspended ()) { if (suspend_count) @@ -8769,9 +8884,7 @@ thread_commands (int command, guint8 *p, guint8 *end, Buffer *buf) } start_frame = decode_int (p, &p, end); - mono_loader_lock (); - tls = (DebuggerTlsData *)mono_g_hash_table_lookup (thread_to_tls, thread); - mono_loader_unlock (); + GET_TLS_DATA_FROM_THREAD (thread); if (tls == NULL) return ERR_UNLOADED; @@ -8784,7 +8897,6 @@ thread_commands (int command, guint8 *p, guint8 *end, Buffer *buf) break; } case CMD_THREAD_GET_FRAME_INFO: { - DebuggerTlsData *tls; int i, start_frame, length; // Wait for suspending if it already started @@ -8805,10 +8917,7 @@ thread_commands (int command, guint8 *p, guint8 *end, Buffer *buf) if (start_frame != 0 || length != -1) return ERR_NOT_IMPLEMENTED; - - mono_loader_lock (); - tls = (DebuggerTlsData *)mono_g_hash_table_lookup (thread_to_tls, thread); - mono_loader_unlock (); + GET_TLS_DATA_FROM_THREAD (thread); if (tls == NULL) return ERR_UNLOADED; @@ -8980,7 +9089,6 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf) MonoThread *thread_obj; MonoInternalThread *thread; int pos, i, len, frame_idx; - DebuggerTlsData *tls; StackFrame *frame; MonoDebugMethodJitInfo *jit; MonoMethodSignature *sig; @@ -8997,9 +9105,7 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf) id = decode_id (p, &p, end); - mono_loader_lock (); - tls = (DebuggerTlsData *)mono_g_hash_table_lookup (thread_to_tls, thread); - mono_loader_unlock (); + GET_TLS_DATA_FROM_THREAD (thread); g_assert (tls); for (i = 0; i < tls->frame_count; ++i) { @@ -9010,7 +9116,7 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf) return ERR_INVALID_FRAMEID; /* The thread is still running native code, can't get frame variables info */ - if (!tls->really_suspended && !tls->async_state.valid) + if (!tls->really_suspended && !tls->async_state.valid) return ERR_NOT_SUSPENDED; frame_idx = i; frame = tls->frames [frame_idx]; @@ -9574,6 +9680,17 @@ object_commands (int command, guint8 *p, guint8 *end, Buffer *buf) buffer_add_typeid (buf, obj->vtable->domain, mono_class_from_mono_type_internal (((MonoReflectionType*)obj->vtable->type)->type)); buffer_add_domainid (buf, obj->vtable->domain); break; + case MDBGPROT_CMD_OBJECT_REF_DELEGATE_GET_METHOD: + buffer_add_methodid (buf, obj->vtable->domain, ((MonoDelegate *)obj)->method); + break; + case MDBGPROT_CMD_OBJECT_IS_DELEGATE: { + MonoType *type = m_class_get_byval_arg (obj_type); + if (m_class_is_delegate (obj_type) || (type->type == MONO_TYPE_GENERICINST && m_class_is_delegate (type->data.generic_class->container_class))) + buffer_add_byte (buf, TRUE); + else + buffer_add_byte (buf, FALSE); + break; + } default: err = ERR_NOT_IMPLEMENTED; goto exit; @@ -9890,6 +10007,62 @@ wait_for_attach (void) return TRUE; } +ErrorCode +mono_process_dbg_packet (int id, CommandSet command_set, int command, gboolean *no_reply, guint8 *buf, guint8 *end, Buffer *ret_buf) +{ + ErrorCode err; + /* Process the request */ + switch (command_set) { + case CMD_SET_VM: + err = vm_commands (command, id, buf, end, ret_buf); + if (err == ERR_NONE && command == CMD_VM_INVOKE_METHOD) + /* Sent after the invoke is complete */ + *no_reply = TRUE; + break; + case CMD_SET_EVENT_REQUEST: + err = event_commands (command, buf, end, ret_buf); + break; + case CMD_SET_APPDOMAIN: + err = domain_commands (command, buf, end, ret_buf); + break; + case CMD_SET_ASSEMBLY: + err = assembly_commands (command, buf, end, ret_buf); + break; + case CMD_SET_MODULE: + err = module_commands (command, buf, end, ret_buf); + break; + case CMD_SET_FIELD: + err = field_commands (command, buf, end, ret_buf); + break; + case CMD_SET_TYPE: + err = type_commands (command, buf, end, ret_buf); + break; + case CMD_SET_METHOD: + err = method_commands (command, buf, end, ret_buf); + break; + case CMD_SET_THREAD: + err = thread_commands (command, buf, end, ret_buf); + break; + case CMD_SET_STACK_FRAME: + err = frame_commands (command, buf, end, ret_buf); + break; + case CMD_SET_ARRAY_REF: + err = array_commands (command, buf, end, ret_buf); + break; + case CMD_SET_STRING_REF: + err = string_commands (command, buf, end, ret_buf); + break; + case CMD_SET_POINTER: + err = pointer_commands (command, buf, end, ret_buf); + break; + case CMD_SET_OBJECT_REF: + err = object_commands (command, buf, end, ret_buf); + break; + default: + err = ERR_NOT_IMPLEMENTED; + } + return err; +} /* * debugger_thread: * @@ -9985,57 +10158,7 @@ debugger_thread (void *arg) err = ERR_NONE; no_reply = FALSE; - - /* Process the request */ - switch (command_set) { - case CMD_SET_VM: - err = vm_commands (command, id, p, end, &buf); - if (err == ERR_NONE && command == CMD_VM_INVOKE_METHOD) - /* Sent after the invoke is complete */ - no_reply = TRUE; - break; - case CMD_SET_EVENT_REQUEST: - err = event_commands (command, p, end, &buf); - break; - case CMD_SET_APPDOMAIN: - err = domain_commands (command, p, end, &buf); - break; - case CMD_SET_ASSEMBLY: - err = assembly_commands (command, p, end, &buf); - break; - case CMD_SET_MODULE: - err = module_commands (command, p, end, &buf); - break; - case CMD_SET_FIELD: - err = field_commands (command, p, end, &buf); - break; - case CMD_SET_TYPE: - err = type_commands (command, p, end, &buf); - break; - case CMD_SET_METHOD: - err = method_commands (command, p, end, &buf); - break; - case CMD_SET_THREAD: - err = thread_commands (command, p, end, &buf); - break; - case CMD_SET_STACK_FRAME: - err = frame_commands (command, p, end, &buf); - break; - case CMD_SET_ARRAY_REF: - err = array_commands (command, p, end, &buf); - break; - case CMD_SET_STRING_REF: - err = string_commands (command, p, end, &buf); - break; - case CMD_SET_POINTER: - err = pointer_commands (command, p, end, &buf); - break; - case CMD_SET_OBJECT_REF: - err = object_commands (command, p, end, &buf); - break; - default: - err = ERR_NOT_IMPLEMENTED; - } + err = mono_process_dbg_packet (id, command_set, command, &no_reply, p, end, &buf); if (command_set == CMD_SET_VM && command == CMD_VM_START_BUFFERING) { buffer_replies = TRUE; @@ -10106,10 +10229,10 @@ mono_debugger_agent_init (void) cbs.breakpoint_from_context = debugger_agent_breakpoint_from_context; cbs.free_mem_manager = debugger_agent_free_mem_manager; cbs.unhandled_exception = debugger_agent_unhandled_exception; - cbs.handle_exception = debugger_agent_handle_exception; + cbs.handle_exception = mono_debugger_agent_handle_exception; cbs.begin_exception_filter = debugger_agent_begin_exception_filter; cbs.end_exception_filter = debugger_agent_end_exception_filter; - cbs.user_break = debugger_agent_user_break; + cbs.user_break = mono_dbg_debugger_agent_user_break; cbs.debug_log = debugger_agent_debug_log; cbs.debug_log_is_enabled = debugger_agent_debug_log_is_enabled; cbs.send_crash = mono_debugger_agent_send_crash; diff --git a/src/mono/mono/mini/debugger-agent.h b/src/mono/mono/mini/debugger-agent.h index 500e8e610c3b5..bf0a06e2056fe 100644 --- a/src/mono/mono/mini/debugger-agent.h +++ b/src/mono/mono/mini/debugger-agent.h @@ -6,12 +6,45 @@ #define __MONO_DEBUGGER_AGENT_H__ #include "mini.h" +#include "debugger-protocol.h" + #include #define MONO_DBG_CALLBACKS_VERSION (4) // 2. debug_log parameters changed from MonoString* to MonoStringHandle // 3. debug_log parameters changed from MonoStringHandle back to MonoString* +typedef struct _InvokeData InvokeData; + +struct _InvokeData +{ + int id; + int flags; + guint8 *p; + guint8 *endp; + /* This is the context which needs to be restored after the invoke */ + MonoContext ctx; + gboolean has_ctx; + /* + * If this is set, invoke this method with the arguments given by ARGS. + */ + MonoMethod *method; + gpointer *args; + guint32 suspend_count; + int nmethods; + + InvokeData *last_invoke; +}; + +typedef struct { + const char *name; + void (*connect) (const char *address); + void (*close1) (void); + void (*close2) (void); + gboolean (*send) (void *buf, int len); + int (*recv) (void *buf, int len); +} DebuggerTransport; + struct _MonoDebuggerCallbacks { int version; void (*parse_options) (char *options); @@ -46,4 +79,37 @@ mono_debugger_agent_stub_init (void); MONO_API MONO_RT_EXTERNAL_ONLY gboolean mono_debugger_agent_transport_handshake (void); +MONO_API void +mono_debugger_agent_register_transport (DebuggerTransport *trans); + +MdbgProtErrorCode +mono_process_dbg_packet (int id, MdbgProtCommandSet command_set, int command, gboolean *no_reply, guint8 *p, guint8 *end, MdbgProtBuffer *buf); + +void +mono_init_debugger_agent_for_wasm (int log_level); + +void* +mono_dbg_create_breakpoint_events (GPtrArray *ss_reqs, GPtrArray *bp_reqs, MonoJitInfo *ji, MdbgProtEventKind kind); + +void +mono_dbg_process_breakpoint_events (void *_evts, MonoMethod *method, MonoContext *ctx, int il_offset); + +void +mono_dbg_debugger_agent_user_break (void); + +void +mono_wasm_save_thread_context (void); + +DebuggerTlsData* +mono_wasm_get_tls (void); + +MdbgProtErrorCode +mono_do_invoke_method (DebuggerTlsData *tls, MdbgProtBuffer *buf, InvokeData *invoke, guint8 *p, guint8 **endp); + +void +mono_debugger_agent_handle_exception (MonoException *exc, MonoContext *throw_ctx, MonoContext *catch_ctx, StackFrameInfo *catch_frame); + +void +mono_ss_discard_frame_context (void *the_tls); + #endif diff --git a/src/mono/mono/mini/debugger-engine.c b/src/mono/mono/mini/debugger-engine.c index c9d85b9099c73..8c85227e8420c 100644 --- a/src/mono/mono/mini/debugger-engine.c +++ b/src/mono/mono/mini/debugger-engine.c @@ -388,13 +388,6 @@ collect_domain_bp (gpointer key, gpointer value, gpointer user_data) jit_mm_unlock (jit_mm); } -void -mono_de_clear_all_breakpoints (void) -{ - while (breakpoints->len) - mono_de_clear_breakpoint ((MonoBreakpoint*)g_ptr_array_index (breakpoints, 0)); -} - /* * mono_de_set_breakpoint: * diff --git a/src/mono/mono/mini/debugger-engine.h b/src/mono/mono/mini/debugger-engine.h index 24ad575093e69..20fe88fedb8d7 100644 --- a/src/mono/mono/mini/debugger-engine.h +++ b/src/mono/mono/mini/debugger-engine.h @@ -497,7 +497,6 @@ MonoBreakpoint* mono_de_set_breakpoint (MonoMethod *method, long il_offset, Even void mono_de_collect_breakpoints_by_sp (SeqPoint *sp, MonoJitInfo *ji, GPtrArray *ss_reqs, GPtrArray *bp_reqs); void mono_de_clear_breakpoints_for_domain (MonoDomain *domain); void mono_de_add_pending_breakpoints (MonoMethod *method, MonoJitInfo *ji); -void mono_de_clear_all_breakpoints (void); MonoBreakpoint * mono_de_get_breakpoint_by_id (int id); //single stepping @@ -545,3 +544,15 @@ void win32_debugger_log(FILE *stream, const gchar *format, ...); #define PRINT_ERROR_MSG(...) g_printerr (__VA_ARGS__) #define PRINT_MSG(...) g_print (__VA_ARGS__) #endif + +int +mono_ss_create_init_args (SingleStepReq *ss_req, SingleStepArgs *args); + +void +mono_ss_args_destroy (SingleStepArgs *ss_args); + +int +mono_get_this_async_id (DbgEngineStackFrame *frame); + +void +mono_ss_calculate_framecount (void *tls, MonoContext *ctx, gboolean force_use_ctx, DbgEngineStackFrame ***frames, int *nframes); diff --git a/src/mono/mono/mini/debugger-protocol.c b/src/mono/mono/mini/debugger-protocol.c index 1402dae3d36ef..2fe3096608a51 100644 --- a/src/mono/mono/mini/debugger-protocol.c +++ b/src/mono/mono/mini/debugger-protocol.c @@ -56,7 +56,6 @@ m_dbgprot_decode_int (uint8_t *buf, uint8_t **endbuf, uint8_t *limit) { *endbuf = buf + 4; g_assert (*endbuf <= limit); - return (((int)buf [0]) << 24) | (((int)buf [1]) << 16) | (((int)buf [2]) << 8) | (((int)buf [3]) << 0); } diff --git a/src/mono/mono/mini/debugger-protocol.h b/src/mono/mono/mini/debugger-protocol.h index 87d9e232c851a..24e8a2e42c27e 100644 --- a/src/mono/mono/mini/debugger-protocol.h +++ b/src/mono/mono/mini/debugger-protocol.h @@ -34,7 +34,8 @@ typedef enum { MDBGPROT_CMD_VM_START_BUFFERING = 14, MDBGPROT_CMD_VM_STOP_BUFFERING = 15, MDBGPROT_CMD_VM_READ_MEMORY = 16, - MDBGPROT_CMD_VM_WRITE_MEMORY = 17 + MDBGPROT_CMD_VM_WRITE_MEMORY = 17, + MDBGPROT_CMD_GET_ASSEMBLY_BY_NAME = 18 } MdbgProtCmdVM; typedef enum { @@ -173,7 +174,9 @@ typedef enum { MDBGPROT_CMD_METHOD_MAKE_GENERIC_METHOD = 10, MDBGPROT_CMD_METHOD_TOKEN = 11, MDBGPROT_CMD_METHOD_ASSEMBLY = 12, - MDBGPROT_CMD_METHOD_GET_CLASS_TOKEN = 13 + MDBGPROT_CMD_METHOD_GET_CLASS_TOKEN = 13, + MDBGPROT_CMD_METHOD_HAS_ASYNC_DEBUG_INFO = 14, + MDBGPROT_CMD_METHOD_GET_NAME_FULL = 15 } MdbgProtCmdMethod; typedef enum { @@ -197,7 +200,8 @@ typedef enum { MDBGPROT_CMD_TYPE_IS_INITIALIZED = 18, MDBGPROT_CMD_TYPE_CREATE_INSTANCE = 19, MDBGPROT_CMD_TYPE_GET_VALUE_SIZE = 20, - MDBGPROT_CMD_TYPE_GET_VALUES_ICORDBG = 21 + MDBGPROT_CMD_TYPE_GET_VALUES_ICORDBG = 21, + MDBGPROT_CMD_TYPE_GET_PARENTS = 22 } MdbgProtCmdType; typedef enum { @@ -235,7 +239,9 @@ typedef enum { MDBGPROT_CMD_OBJECT_REF_GET_DOMAIN = 5, MDBGPROT_CMD_OBJECT_REF_SET_VALUES = 6, MDBGPROT_CMD_OBJECT_REF_GET_INFO = 7, - MDBGPROT_CMD_OBJECT_REF_GET_VALUES_ICORDBG = 8 + MDBGPROT_CMD_OBJECT_REF_GET_VALUES_ICORDBG = 8, + MDBGPROT_CMD_OBJECT_REF_DELEGATE_GET_METHOD = 9, + MDBGPROT_CMD_OBJECT_IS_DELEGATE = 10 } MdbgProtCmdObject; typedef enum { diff --git a/src/mono/mono/mini/mini-wasm-debugger.c b/src/mono/mono/mini/mini-wasm-debugger.c index 352bb81deb923..fd19c6a846277 100644 --- a/src/mono/mono/mini/mini-wasm-debugger.c +++ b/src/mono/mono/mini/mini-wasm-debugger.c @@ -11,6 +11,8 @@ #include #include #include +#include +#include //XXX This is dirty, extend ee.h to support extracting info from MonoInterpFrameHandle #include @@ -24,84 +26,29 @@ static int log_level = 1; -enum { - EXCEPTION_MODE_NONE, - EXCEPTION_MODE_UNCAUGHT, - EXCEPTION_MODE_ALL -}; - -// Flags for get_*_properties -#define GPFLAG_NONE 0x0000 -#define GPFLAG_OWN_PROPERTIES 0x0001 -#define GPFLAG_ACCESSORS_ONLY 0x0002 -#define GPFLAG_EXPAND_VALUETYPES 0x0004 - //functions exported to be used by JS G_BEGIN_DECLS -EMSCRIPTEN_KEEPALIVE int mono_wasm_set_breakpoint (const char *assembly_name, int method_token, int il_offset); -EMSCRIPTEN_KEEPALIVE int mono_wasm_remove_breakpoint (int bp_id); -EMSCRIPTEN_KEEPALIVE int mono_wasm_current_bp_id (void); -EMSCRIPTEN_KEEPALIVE void mono_wasm_enum_frames (void); -EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_get_local_vars (int scope, int* pos, int len); -EMSCRIPTEN_KEEPALIVE void mono_wasm_clear_all_breakpoints (void); -EMSCRIPTEN_KEEPALIVE int mono_wasm_setup_single_step (int kind); -EMSCRIPTEN_KEEPALIVE int mono_wasm_pause_on_exceptions (int state); -EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_get_object_properties (int object_id, int gpflags); -EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_get_array_values (int object_id, int start_idx, int count, int gpflags); -EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_invoke_getter_on_object (int object_id, const char* name); -EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_invoke_getter_on_value (void *value, MonoClass *klass, const char *name); -EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_get_deref_ptr_value (void *value_addr, MonoClass *klass); EMSCRIPTEN_KEEPALIVE void mono_wasm_set_is_debugger_attached (gboolean is_attached); -EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_set_variable_on_frame (int scope, int index, const char* name, const char* value); -EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_set_value_on_object (int object_id, const char* name, const char* value); +EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_send_dbg_command (int id, MdbgProtCommandSet command_set, int command, guint8* data, unsigned int size); +EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_send_dbg_command_with_parms (int id, MdbgProtCommandSet command_set, int command, guint8* data, unsigned int size, int valtype, char* newvalue); + //JS functions imported that we use -extern void mono_wasm_add_frame (int il_offset, int method_token, int frame_id, const char *assembly_name, const char *method_name); -extern void mono_wasm_fire_bp (void); -extern void mono_wasm_fire_exception (int exception_obj_id, const char* message, const char* class_name, gboolean uncaught); -extern void mono_wasm_add_obj_var (const char*, const char*, guint64); -extern void mono_wasm_add_enum_var (const char*, const char*, guint64); -extern void mono_wasm_add_func_var (const char*, const char*, guint64); -extern void mono_wasm_add_properties_var (const char*, gint32); -extern void mono_wasm_add_array_item (int); -extern void mono_wasm_set_is_async_method (guint64); -extern void mono_wasm_add_typed_value (const char *type, const char *str_value, double value); +extern void mono_wasm_fire_debugger_agent_message (void); extern void mono_wasm_asm_loaded (const char *asm_name, const char *assembly_data, guint32 assembly_len, const char *pdb_data, guint32 pdb_len); G_END_DECLS -static void describe_object_properties_for_klass (void *obj, MonoClass *klass, gboolean isAsyncLocalThis, int gpflags); static void handle_exception (MonoException *exc, MonoContext *throw_ctx, MonoContext *catch_ctx, StackFrameInfo *catch_frame); +static gboolean receive_debugger_agent_message (void *data, int len); static void assembly_loaded (MonoProfiler *prof, MonoAssembly *assembly); -static MonoObject* mono_runtime_try_invoke_internal (MonoMethod *method, void *obj, void **params, MonoObject **exc, MonoError* error); //FIXME move all of those fields to the profiler object static gboolean debugger_enabled; static gboolean has_pending_lazy_loaded_assemblies; -static int event_request_id; -static GHashTable *objrefs; -static GHashTable *obj_to_objref; -static int objref_id = 0; -static int pause_on_exc = EXCEPTION_MODE_NONE; -static MonoObject* exception_on_runtime_invoke = NULL; - -static const char* -all_getters_allowed_class_names[] = { - "System.DateTime", - "System.DateTimeOffset", - "System.TimeSpan" -}; - -static const char* -to_string_as_descr_names[] = { - "System.DateTime", - "System.DateTimeOffset", - "System.Decimal", - "System.TimeSpan" -}; #define THREAD_TO_INTERNAL(thread) (thread)->internal_thread @@ -129,14 +76,6 @@ void wasm_debugger_log (int level, const gchar *format, ...) g_free (mesg); } -static void -inplace_tolower (char *c) -{ - int i; - for (i = strlen (c) - 1; i >= 0; --i) - c [i] = tolower (c [i]); -} - static void jit_done (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo) { @@ -149,78 +88,6 @@ appdomain_load (MonoProfiler *prof, MonoDomain *domain) mono_de_domain_add (domain); } -/* Frame state handling */ -static GPtrArray *frames; - -static void -free_frame (DbgEngineStackFrame *frame) -{ - g_free (frame); -} - -static gboolean -collect_frames (MonoStackFrameInfo *info, MonoContext *ctx, gpointer data) -{ - SeqPoint sp; - MonoMethod *method; - - //skip wrappers - if (info->type != FRAME_TYPE_MANAGED && info->type != FRAME_TYPE_INTERP) - return FALSE; - - if (info->ji) - method = jinfo_get_method (info->ji); - else - method = info->method; - - if (!method) - return FALSE; - - PRINT_DEBUG_MSG (2, "collect_frames: Reporting method %s native_offset %d, wrapper_type: %d\n", method->name, info->native_offset, method->wrapper_type); - - if (!mono_find_prev_seq_point_for_native_offset (method, info->native_offset, NULL, &sp)) - PRINT_DEBUG_MSG (2, "collect_frames: Failed to lookup sequence point. method: %s, native_offset: %d \n", method->name, info->native_offset); - - - StackFrame *frame = g_new0 (StackFrame, 1); - frame->de.ji = info->ji; - frame->de.domain = mono_get_root_domain (); - frame->de.method = method; - frame->de.native_offset = info->native_offset; - - frame->il_offset = info->il_offset; - frame->interp_frame = info->interp_frame; - frame->frame_addr = info->frame_addr; - - g_ptr_array_add (frames, frame); - - return FALSE; -} - -static void -free_frame_state (void) -{ - if (frames) { - int i; - for (i = 0; i < frames->len; ++i) - free_frame ((DbgEngineStackFrame*)g_ptr_array_index (frames, i)); - g_ptr_array_set_size (frames, 0); - } -} - -static void -compute_frames (void) { - if (frames) { - int i; - for (i = 0; i < frames->len; ++i) - free_frame ((DbgEngineStackFrame*)g_ptr_array_index (frames, i)); - g_ptr_array_set_size (frames, 0); - } else { - frames = g_ptr_array_new (); - } - - mono_walk_stack_with_ctx (collect_frames, NULL, MONO_UNWIND_NONE, NULL); -} static MonoContext* tls_get_restore_state (void *tls) { @@ -247,17 +114,14 @@ begin_single_step_processing (MonoContext *ctx, gboolean from_signal) static void ss_discard_frame_context (void *the_tls) { - free_frame_state (); + mono_ss_discard_frame_context (mono_wasm_get_tls()); } static void ss_calculate_framecount (void *tls, MonoContext *ctx, gboolean force_use_ctx, DbgEngineStackFrame ***out_frames, int *nframes) { - compute_frames (); - if (out_frames) - *out_frames = (DbgEngineStackFrame **)frames->pdata; - if (nframes) - *nframes = frames->len; + mono_wasm_save_thread_context(); + mono_ss_calculate_framecount(mono_wasm_get_tls(), NULL, force_use_ctx, out_frames, nframes); } static gboolean @@ -272,132 +136,8 @@ ensure_runtime_is_suspended (void) return DE_ERR_NONE; } -static int -get_object_id (MonoObject *obj) -{ - ObjRef *ref; - if (!obj) - return 0; - - ref = (ObjRef *)g_hash_table_lookup (obj_to_objref, GINT_TO_POINTER (~((gsize)obj))); - if (ref) - return ref->id; - ref = g_new0 (ObjRef, 1); - ref->id = mono_atomic_inc_i32 (&objref_id); - ref->handle = mono_gchandle_new_weakref_internal (obj, FALSE); - g_hash_table_insert (objrefs, GINT_TO_POINTER (ref->id), ref); - g_hash_table_insert (obj_to_objref, GINT_TO_POINTER (~((gsize)obj)), ref); - return ref->id; -} - - -static int -get_this_async_id (DbgEngineStackFrame *frame) -{ - MonoClassField *builder_field; - gpointer builder; - MonoMethod *method; - MonoObject *ex; - ERROR_DECL (error); - MonoObject *obj; - - /* - * FRAME points to a method in a state machine class/struct. - * Call the ObjectIdForDebugger method of the associated method builder type. - */ - builder = get_async_method_builder (frame); - if (!builder) - return 0; - - builder_field = mono_class_get_field_from_name_full (get_class_to_get_builder_field(frame), "<>t__builder", NULL); - if (!builder_field) - return 0; - - method = get_object_id_for_debugger_method (mono_class_from_mono_type_internal (builder_field->type)); - if (!method) { - return 0; - } - - obj = mono_runtime_try_invoke_internal (method, builder, NULL, &ex, error); - mono_error_assert_ok (error); - - return get_object_id (obj); -} - -typedef struct { - gboolean is_ss; //do I need this? -} BpEvents; - -static void* -create_breakpoint_events (GPtrArray *ss_reqs, GPtrArray *bp_reqs, MonoJitInfo *ji, EventKind kind) -{ - PRINT_DEBUG_MSG (1, "ss_reqs %d bp_reqs %d\n", ss_reqs->len, bp_reqs->len); - if ((ss_reqs && ss_reqs->len) || (bp_reqs && bp_reqs->len)) { - BpEvents *evts = g_new0 (BpEvents, 1); //just a non-null value to make sure we can raise it on process_breakpoint_events - evts->is_ss = (ss_reqs && ss_reqs->len); - return evts; - } - return NULL; -} - -static void -process_breakpoint_events (void *_evts, MonoMethod *method, MonoContext *ctx, int il_offsets) -{ - BpEvents *evts = (BpEvents*)_evts; - if (evts) { - if (evts->is_ss) - mono_de_cancel_all_ss (); - mono_wasm_fire_bp (); - g_free (evts); - } -} - -static void -no_seq_points_found (MonoMethod *method, int offset) -{ - /* - * This can happen in full-aot mode with assemblies AOTed without the 'soft-debug' option to save space. - */ - PRINT_DEBUG_MSG (1, "Unable to find seq points for method '%s', offset 0x%x.\n", mono_method_full_name (method, TRUE), offset); -} - #define DBG_NOT_SUSPENDED 1 -static int -ss_create_init_args (SingleStepReq *ss_req, SingleStepArgs *ss_args) -{ - PRINT_DEBUG_MSG (1, "ss_create_init_args\n"); - int dummy = 0; - ss_req->start_sp = ss_req->last_sp = &dummy; - compute_frames (); - memset (ss_args, 0, sizeof (*ss_args)); - - // This shouldn't happen - maybe should assert here ? - if (frames->len == 0) { - PRINT_DEBUG_MSG (1, "SINGLE STEPPING FOUND NO FRAMES"); - return DBG_NOT_SUSPENDED; - } - - DbgEngineStackFrame *frame = (DbgEngineStackFrame*)g_ptr_array_index (frames, 0); - ss_req->start_method = ss_args->method = frame->method; - gboolean found_sp = mono_find_prev_seq_point_for_native_offset (frame->method, frame->native_offset, &ss_args->info, &ss_args->sp); - if (!found_sp) - no_seq_points_found (frame->method, frame->native_offset); - g_assert (found_sp); - - ss_args->frames = (DbgEngineStackFrame**)frames->pdata; - ss_args->nframes = frames->len; - //XXX do sp - - return DE_ERR_NONE; -} - -static void -ss_args_destroy (SingleStepArgs *ss_args) -{ - //nothing to do -} - static int handle_multiple_ss_requests (void) { mono_de_cancel_all_ss (); @@ -419,13 +159,13 @@ mono_wasm_debugger_init (void) .ss_calculate_framecount = ss_calculate_framecount, .ensure_jit = ensure_jit, .ensure_runtime_is_suspended = ensure_runtime_is_suspended, - .get_this_async_id = get_this_async_id, + .get_this_async_id = mono_get_this_async_id, .set_set_notification_for_wait_completion_flag = set_set_notification_for_wait_completion_flag, .get_notify_debugger_of_wait_completion_method = get_notify_debugger_of_wait_completion_method, - .create_breakpoint_events = create_breakpoint_events, - .process_breakpoint_events = process_breakpoint_events, - .ss_create_init_args = ss_create_init_args, - .ss_args_destroy = ss_args_destroy, + .create_breakpoint_events = mono_dbg_create_breakpoint_events, + .process_breakpoint_events = mono_dbg_process_breakpoint_events, + .ss_create_init_args = mono_ss_create_init_args, + .ss_args_destroy = mono_ss_args_destroy, .handle_multiple_ss_requests = handle_multiple_ss_requests, }; @@ -444,11 +184,16 @@ mono_wasm_debugger_init (void) mono_profiler_set_domain_loaded_callback (prof, appdomain_load); mono_profiler_set_assembly_loaded_callback (prof, assembly_loaded); - obj_to_objref = g_hash_table_new (NULL, NULL); - objrefs = g_hash_table_new_full (NULL, NULL, NULL, mono_debugger_free_objref); + mini_get_dbg_callbacks ()->handle_exception = mono_debugger_agent_handle_exception; + mini_get_dbg_callbacks ()->user_break = mono_dbg_debugger_agent_user_break; + +//debugger-agent initialization + DebuggerTransport trans; + trans.name = "buffer-wasm-communication"; + trans.send = receive_debugger_agent_message; - mini_get_dbg_callbacks ()->handle_exception = handle_exception; - mini_get_dbg_callbacks ()->user_break = mono_wasm_user_break; + mono_debugger_agent_register_transport (&trans); + mono_init_debugger_agent_for_wasm (log_level); } MONO_API void @@ -459,69 +204,6 @@ mono_wasm_enable_debugging (int debug_level) log_level = debug_level; } -EMSCRIPTEN_KEEPALIVE int -mono_wasm_pause_on_exceptions (int state) -{ - pause_on_exc = state; - PRINT_DEBUG_MSG (1, "setting pause on exception: %d\n", pause_on_exc); - return 1; -} - -EMSCRIPTEN_KEEPALIVE int -mono_wasm_setup_single_step (int kind) -{ - int nmodifiers = 1; - - PRINT_DEBUG_MSG (2, ">>>> mono_wasm_setup_single_step %d\n", kind); - EventRequest *req = (EventRequest *)g_malloc0 (sizeof (EventRequest) + (nmodifiers * sizeof (Modifier))); - req->id = ++event_request_id; - req->event_kind = EVENT_KIND_STEP; - // DE doesn't care about suspend_policy - // req->suspend_policy = SUSPEND_POLICY_ALL; - req->nmodifiers = nmodifiers; - - StepSize size = STEP_SIZE_MIN; - - //FIXME I DON'T KNOW WHAT I'M DOING!!!!! filter all the things. - StepFilter filter = (StepFilter)(STEP_FILTER_STATIC_CTOR | STEP_FILTER_DEBUGGER_HIDDEN | STEP_FILTER_DEBUGGER_STEP_THROUGH | STEP_FILTER_DEBUGGER_NON_USER_CODE); - req->modifiers [0].data.filter = filter; - - StepDepth depth; - switch (kind) { - case 0: //into - depth = STEP_DEPTH_INTO; - break; - case 1: //out - depth = STEP_DEPTH_OUT; - break; - case 2: //over - depth = STEP_DEPTH_OVER; - break; - default: - g_error ("[dbg] unknown step kind %d", kind); - } - - DbgEngineErrorCode err = mono_de_ss_create (THREAD_TO_INTERNAL (mono_thread_current ()), size, depth, filter, req); - if (err != DE_ERR_NONE) { - PRINT_DEBUG_MSG (1, "[dbg] Failed to setup single step request"); - } - PRINT_DEBUG_MSG (1, "[dbg] single step is in place, now what?\n"); - SingleStepReq *ss_req = req->info; - int isBPOnNativeCode = 0; - if (ss_req && ss_req->bps) { - GSList *l; - - for (l = ss_req->bps; l; l = l->next) { - if (((MonoBreakpoint *)l->data)->method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE) - isBPOnNativeCode = 1; - } - } - if (!isBPOnNativeCode) { - mono_de_cancel_all_ss (); - } - return isBPOnNativeCode; -} - static void assembly_loaded (MonoProfiler *prof, MonoAssembly *assembly) { @@ -551,1403 +233,203 @@ assembly_loaded (MonoProfiler *prof, MonoAssembly *assembly) } } -static void -handle_exception (MonoException *exc, MonoContext *throw_ctx, MonoContext *catch_ctx, StackFrameInfo *catch_frame) -{ - ERROR_DECL (error); - const char *default_error_message = "Failed to get exception message."; - - PRINT_DEBUG_MSG (1, "handle exception - %d - %p - %p - %p\n", pause_on_exc, exc, throw_ctx, catch_ctx); - - //normal mono_runtime_try_invoke does not capture the exception and this is a temporary workaround. - exception_on_runtime_invoke = (MonoObject*)exc; - - if (pause_on_exc == EXCEPTION_MODE_NONE) - return; - if (pause_on_exc == EXCEPTION_MODE_UNCAUGHT && catch_ctx != NULL) - return; - - int obj_id = get_object_id ((MonoObject *)exc); - char *error_message = mono_string_to_utf8_checked_internal (exc->message, error); - const char *class_name = mono_class_full_name (mono_object_class (exc)); - PRINT_DEBUG_MSG (2, "handle exception - calling mono_wasm_fire_exc(): %d - message - %s, class_name: %s\n", obj_id, !is_ok (error) ? error_message : default_error_message, class_name); - - mono_wasm_fire_exception (obj_id, !is_ok (error) ? error_message : default_error_message, class_name, !catch_ctx); - - if (error_message != NULL) - g_free (error_message); - - PRINT_DEBUG_MSG (2, "handle exception - done\n"); +void +mono_wasm_single_step_hit (void) +{ + mono_de_process_single_step (mono_wasm_get_tls(), FALSE); } - -EMSCRIPTEN_KEEPALIVE void -mono_wasm_clear_all_breakpoints (void) +void +mono_wasm_breakpoint_hit (void) { - PRINT_DEBUG_MSG (1, "CLEAR BREAKPOINTS\n"); - mono_de_clear_all_breakpoints (); + mono_de_process_breakpoint (mono_wasm_get_tls(), FALSE); } -EMSCRIPTEN_KEEPALIVE int -mono_wasm_set_breakpoint (const char *assembly_name, int method_token, int il_offset) +static gboolean +write_value_to_buffer (MdbgProtBuffer *buf, MonoTypeEnum type, const char* variableValue) { - int i; - ERROR_DECL (error); - PRINT_DEBUG_MSG (1, "SET BREAKPOINT: assembly %s method %x offset %x\n", assembly_name, method_token, il_offset); - - - //we get 'foo.dll' but mono_assembly_load expects 'foo' so we strip the last dot - char *lookup_name = g_strdup (assembly_name); - for (i = strlen (lookup_name) - 1; i >= 0; --i) { - if (lookup_name [i] == '.') { - lookup_name [i] = 0; + char* endptr; + errno = 0; + buffer_add_byte (buf, type); + switch (type) { + case MONO_TYPE_BOOLEAN: + if (!strcasecmp (variableValue, "True")) + buffer_add_int (buf, 1); + else if (!strcasecmp (variableValue, "False")) + buffer_add_int (buf, 0); + else + return FALSE; + break; + case MONO_TYPE_CHAR: + if (strlen (variableValue) > 1) + return FALSE; + buffer_add_int (buf, (variableValue [0])); + break; + case MONO_TYPE_I1: { + intmax_t val = strtoimax (variableValue, &endptr, 10); + if (errno != 0) + return FALSE; + if (val >= -128 && val <= 127) + buffer_add_int (buf, val); + else + return FALSE; break; } + case MONO_TYPE_U1: { + intmax_t val = strtoimax (variableValue, &endptr, 10); + if (errno != 0) + return FALSE; + if (val >= 0 && val <= 255) + buffer_add_int (buf, val); + else + return FALSE; + break; + } + case MONO_TYPE_I2: { + intmax_t val = strtoimax (variableValue, &endptr, 10); + if (errno != 0) + return FALSE; + if (val >= -32768 && val <= 32767) + buffer_add_int (buf, val); + else + return FALSE; + break; + } + case MONO_TYPE_U2: { + intmax_t val = strtoimax (variableValue, &endptr, 10); + if (errno != 0) + return FALSE; + if (val >= 0 && val <= 65535) + buffer_add_int (buf, val); + else + return FALSE; + break; + } + case MONO_TYPE_I4: { + intmax_t val = strtoimax (variableValue, &endptr, 10); + if (errno != 0) + return FALSE; + if (val >= -2147483648 && val <= 2147483647) + buffer_add_int (buf, val); + else + return FALSE; + break; + } + case MONO_TYPE_U4: { + intmax_t val = strtoimax (variableValue, &endptr, 10); + if (errno != 0) + return FALSE; + if (val >= 0 && val <= 4294967295) + buffer_add_int (buf, val); + else + return FALSE; + break; + } + case MONO_TYPE_I8: { + long long val = strtoll (variableValue, &endptr, 10); + if (errno != 0) + return FALSE; + buffer_add_long (buf, val); + break; + } + case MONO_TYPE_U8: { + long long val = strtoll (variableValue, &endptr, 10); + if (errno != 0) + return FALSE; + buffer_add_long (buf, val); + break; + } + case MONO_TYPE_R4: { + gfloat val = strtof (variableValue, &endptr); + if (errno != 0) + return FALSE; + buffer_add_int (buf, *((gint32*)(&val))); + break; + } + case MONO_TYPE_R8: { + gdouble val = strtof (variableValue, &endptr); + if (errno != 0) + return FALSE; + buffer_add_long (buf, *((guint64*)(&val))); + break; + } + default: + return FALSE; } - - //resolve the assembly - MonoImageOpenStatus status; - MonoAssemblyName* aname = mono_assembly_name_new (lookup_name); - MonoAssemblyByNameRequest byname_req; - mono_assembly_request_prepare_byname (&byname_req, MONO_ASMCTX_DEFAULT, mono_alc_get_default ()); - MonoAssembly *assembly = mono_assembly_request_byname (aname, &byname_req, &status); - g_free (lookup_name); - if (!assembly) { - PRINT_DEBUG_MSG (1, "Could not resolve assembly %s\n", assembly_name); - return -1; - } - - mono_assembly_name_free_internal (aname); - - MonoMethod *method = mono_get_method_checked (assembly->image, MONO_TOKEN_METHOD_DEF | method_token, NULL, NULL, error); - if (!method) { - //FIXME don't swallow the error - PRINT_DEBUG_MSG (1, "Could not find method due to %s\n", mono_error_get_message (error)); - mono_error_cleanup (error); - return -1; - } - - //FIXME right now none of the EventRequest fields are used by debugger-engine - EventRequest *req = g_new0 (EventRequest, 1); - req->id = ++event_request_id; - req->event_kind = EVENT_KIND_BREAKPOINT; - //DE doesn't care about suspend_policy - // req->suspend_policy = SUSPEND_POLICY_ALL; - req->nmodifiers = 0; //funny thing, - - // BreakPointRequest *req = breakpoint_request_new (assembly, method, il_offset); - MonoBreakpoint *bp = mono_de_set_breakpoint (method, il_offset, req, error); - - if (!bp) { - PRINT_DEBUG_MSG (1, "Could not set breakpoint to %s\n", mono_error_get_message (error)); - mono_error_cleanup (error); - return 0; - } - - PRINT_DEBUG_MSG (1, "NEW BP %p has id %d\n", req, req->id); - return req->id; -} - -EMSCRIPTEN_KEEPALIVE int -mono_wasm_remove_breakpoint (int bp_id) -{ - MonoBreakpoint *bp = mono_de_get_breakpoint_by_id (bp_id); - if (!bp) - return 0; - - mono_de_clear_breakpoint (bp); - return 1; + return TRUE; } -void -mono_wasm_single_step_hit (void) +EMSCRIPTEN_KEEPALIVE void +mono_wasm_set_is_debugger_attached (gboolean is_attached) { - mono_de_process_single_step (NULL, FALSE); + mono_set_is_debugger_attached (is_attached); + if (is_attached && has_pending_lazy_loaded_assemblies) + { + GPtrArray *assemblies = mono_alc_get_all_loaded_assemblies (); + for (int i = 0; i < assemblies->len; ++i) { + MonoAssembly *ass = (MonoAssembly*)g_ptr_array_index (assemblies, i); + assembly_loaded (NULL, ass); + } + g_ptr_array_free (assemblies, TRUE); + has_pending_lazy_loaded_assemblies = FALSE; + } } -void -mono_wasm_breakpoint_hit (void) +EMSCRIPTEN_KEEPALIVE gboolean +mono_wasm_send_dbg_command_with_parms (int id, MdbgProtCommandSet command_set, int command, guint8* data, unsigned int size, int valtype, char* newvalue) { - mono_de_process_breakpoint (NULL, FALSE); - // mono_wasm_fire_bp (); + MdbgProtBuffer bufWithParms; + buffer_init (&bufWithParms, 128); + m_dbgprot_buffer_add_data (&bufWithParms, data, size); + if (!write_value_to_buffer(&bufWithParms, valtype, newvalue)) { + EM_ASM ({ + MONO.mono_wasm_add_dbg_command_received ($0, $1, $2, $3); + }, 0, id, 0, 0); + return TRUE; + } + mono_wasm_send_dbg_command(id, command_set, command, bufWithParms.buf, m_dbgprot_buffer_len(&bufWithParms)); + buffer_free (&bufWithParms); + return TRUE; } -void -mono_wasm_user_break (void) +EMSCRIPTEN_KEEPALIVE gboolean +mono_wasm_send_dbg_command (int id, MdbgProtCommandSet command_set, int command, guint8* data, unsigned int size) { - mono_wasm_fire_bp (); + ss_calculate_framecount (NULL, NULL, TRUE, NULL, NULL); + MdbgProtBuffer buf; + buffer_init (&buf, 128); + gboolean no_reply; + MdbgProtErrorCode error = 0; + if (command_set == MDBGPROT_CMD_SET_VM && command == MDBGPROT_CMD_VM_INVOKE_METHOD ) + { + DebuggerTlsData* tls = mono_wasm_get_tls(); + InvokeData invoke_data; + memset(&invoke_data, 0, sizeof(InvokeData)); + invoke_data.endp = data + size; + error = mono_do_invoke_method(tls, &buf, &invoke_data, data, &data); + } + else + error = mono_process_dbg_packet(id, command_set, command, &no_reply, data, data + size, &buf); + EM_ASM ({ + MONO.mono_wasm_add_dbg_command_received ($0, $1, $2, $3); + }, error == MDBGPROT_ERR_NONE, id, buf.buf, buf.p-buf.buf); + + buffer_free (&buf); + return TRUE; } -EMSCRIPTEN_KEEPALIVE int -mono_wasm_current_bp_id (void) +static gboolean +receive_debugger_agent_message (void *data, int len) { - PRINT_DEBUG_MSG (2, "COMPUTING breakpoint ID\n"); - //FIXME handle compiled case - - /* Interpreter */ - MonoLMF *lmf = mono_get_lmf (); - - g_assert (((guint64)lmf->previous_lmf) & 2); - MonoLMFExt *ext = (MonoLMFExt*)lmf; - - g_assert (ext->kind == MONO_LMFEXT_INTERP_EXIT || ext->kind == MONO_LMFEXT_INTERP_EXIT_WITH_CTX); - MonoInterpFrameHandle *frame = (MonoInterpFrameHandle*)ext->interp_exit_data; - MonoJitInfo *ji = mini_get_interp_callbacks ()->frame_get_jit_info (frame); - guint8 *ip = (guint8*)mini_get_interp_callbacks ()->frame_get_ip (frame); - - g_assert (ji && !ji->is_trampoline); - MonoMethod *method = jinfo_get_method (ji); - - /* Compute the native offset of the breakpoint from the ip */ - guint32 native_offset = ip - (guint8*)ji->code_start; - - MonoSeqPointInfo *info = NULL; - SeqPoint sp; - gboolean found_sp = mono_find_prev_seq_point_for_native_offset (method, native_offset, &info, &sp); - if (!found_sp) - PRINT_DEBUG_MSG (1, "Could not find SP\n"); - - - GPtrArray *bp_reqs = g_ptr_array_new (); - mono_de_collect_breakpoints_by_sp (&sp, ji, NULL, bp_reqs); - - if (bp_reqs->len == 0) { - PRINT_DEBUG_MSG (1, "BP NOT FOUND for method %s JI %p il_offset %d\n", method->name, ji, sp.il_offset); - return -1; - } - - if (bp_reqs->len > 1) - PRINT_DEBUG_MSG (1, "Multiple breakpoints (%d) at the same location, returning the first one.", bp_reqs->len); - - EventRequest *evt = (EventRequest *)g_ptr_array_index (bp_reqs, 0); - g_ptr_array_free (bp_reqs, TRUE); - - PRINT_DEBUG_MSG (1, "Found BP %p with id %d\n", evt, evt->id); - return evt->id; -} - -static MonoObject* -get_object_from_id (int objectId) -{ - ObjRef *ref = (ObjRef *)g_hash_table_lookup (objrefs, GINT_TO_POINTER (objectId)); - if (!ref) { - PRINT_DEBUG_MSG (2, "get_object_from_id !ref: %d\n", objectId); - return NULL; - } - - MonoObject *obj = mono_gchandle_get_target_internal (ref->handle); - if (!obj) - PRINT_DEBUG_MSG (2, "get_object_from_id !obj: %d\n", objectId); - - return obj; -} - -static gboolean -list_frames (MonoStackFrameInfo *info, MonoContext *ctx, gpointer data) -{ - SeqPoint sp; - MonoMethod *method; - char *method_full_name; - - int* frame_id_p = (int*)data; - (*frame_id_p)++; - - //skip wrappers - if (info->type != FRAME_TYPE_MANAGED && info->type != FRAME_TYPE_INTERP) - return FALSE; - - if (info->ji) - method = jinfo_get_method (info->ji); - else - method = info->method; - - if (!method || method->wrapper_type != MONO_WRAPPER_NONE) - return FALSE; - - PRINT_DEBUG_MSG (2, "list_frames: Reporting method %s native_offset %d, wrapper_type: %d\n", method->name, info->native_offset, method->wrapper_type); - - if (!mono_find_prev_seq_point_for_native_offset (method, info->native_offset, NULL, &sp)) - PRINT_DEBUG_MSG (2, "list_frames: Failed to lookup sequence point. method: %s, native_offset: %d\n", method->name, info->native_offset); - - method_full_name = mono_method_full_name (method, FALSE); - while (method->is_inflated) - method = ((MonoMethodInflated*)method)->declaring; - - char *assembly_name = g_strdup (m_class_get_image (method->klass)->module_name); - inplace_tolower (assembly_name); - - PRINT_DEBUG_MSG (2, "adding off %d token %d assembly name %s\n", sp.il_offset, mono_metadata_token_index (method->token), assembly_name); - mono_wasm_add_frame (sp.il_offset, mono_metadata_token_index (method->token), *frame_id_p, assembly_name, method_full_name); - - g_free (assembly_name); - - return FALSE; -} - -EMSCRIPTEN_KEEPALIVE void -mono_wasm_enum_frames (void) -{ - int frame_id = -1; - mono_walk_stack_with_ctx (list_frames, NULL, MONO_UNWIND_NONE, &frame_id); -} - -static char* -invoke_to_string (const char *class_name, MonoClass *klass, gpointer addr) -{ - MonoObject *exc; - MonoString *mstr; - char *ret_str; - ERROR_DECL (error); - MonoObject *obj; - - // TODO: this is for a specific use case right now, - // (invoke ToString() get a preview/description for *some* types) - // and we don't want to report errors for that. - if (m_class_is_valuetype (klass)) { - MonoMethod *method; - - MONO_STATIC_POINTER_INIT (MonoMethod, to_string) - to_string = mono_class_get_method_from_name_checked (mono_get_object_class (), "ToString", 0, METHOD_ATTRIBUTE_VIRTUAL | METHOD_ATTRIBUTE_PUBLIC, error); - mono_error_assert_ok (error); - MONO_STATIC_POINTER_INIT_END (MonoMethod, to_string) - - method = mono_class_get_virtual_method (klass, to_string, error); - if (!method) - return NULL; - - MonoString *mstr = (MonoString*) mono_runtime_try_invoke_internal (method, addr , NULL, &exc, error); - if (exc || !is_ok (error)) { - PRINT_DEBUG_MSG (1, "Failed to invoke ToString for %s\n", class_name); - return NULL; - } - - return mono_string_to_utf8_checked_internal (mstr, error); - } - - obj = *(MonoObject**)addr; - if (!obj) - return NULL; - - mstr = mono_object_try_to_string (obj, &exc, error); - if (exc || !is_ok (error)) - return NULL; - - ret_str = mono_string_to_utf8_checked_internal (mstr, error); - if (!is_ok (error)) - return NULL; - - return ret_str; -} - -static char* -get_to_string_description (const char* class_name, MonoClass *klass, gpointer addr) -{ - if (!class_name || !klass || !addr) - return NULL; - - if (strcmp (class_name, "System.Guid") == 0) - return mono_guid_to_string (addr); - - for (int i = 0; i < G_N_ELEMENTS (to_string_as_descr_names); i ++) { - if (strcmp (to_string_as_descr_names [i], class_name) == 0) { - return invoke_to_string (class_name, klass, addr); - } - } - - return NULL; -} - -typedef struct { - int cur_frame; - int target_frame; - int len; - int *pos; - gboolean found; -} FrameDescData; - - -typedef struct { - int cur_frame; - int target_frame; - int pos; - const char* new_value; - gboolean found; - gboolean error; -} SetVariableValueData; - -/* - * this returns a string formatted like - * - * :[]: - * - * .. which is consumed by `mono_wasm_add_func_var`. It is used for - * generating this for the delegate, and it's target. - */ -static char* -mono_method_to_desc_for_js (MonoMethod *method, gboolean include_namespace) -{ - MonoMethodSignature *sig = mono_method_signature_internal (method); - char *ret_desc = mono_type_full_name (sig->ret); - char *args_desc = mono_signature_get_desc (sig, include_namespace); - - char *sig_desc = g_strdup_printf ("%s:%s:%s", ret_desc, args_desc, method->name); - - g_free (ret_desc); - g_free (args_desc); - return sig_desc; -} - -static guint64 -read_enum_value (const char *mem, int type) -{ - switch (type) { - case MONO_TYPE_BOOLEAN: - case MONO_TYPE_U1: - return *(guint8*)mem; - case MONO_TYPE_I1: - return *(gint8*)mem; - case MONO_TYPE_CHAR: - case MONO_TYPE_U2: - return read16 (mem); - case MONO_TYPE_I2: - return (gint16) read16 (mem); - case MONO_TYPE_U4: - case MONO_TYPE_R4: - return read32 (mem); - case MONO_TYPE_I4: - return (gint32) read32 (mem); - case MONO_TYPE_U8: - case MONO_TYPE_I8: - case MONO_TYPE_R8: - return read64 (mem); - case MONO_TYPE_U: - case MONO_TYPE_I: -#if SIZEOF_REGISTER == 8 - return read64 (mem); -#else - return read32 (mem); -#endif - default: - g_assert_not_reached (); - } - return 0; -} - -static gboolean -nullable_try_get_value (guint8 *nullable, MonoClass *klass, gpointer* out_value) -{ - mono_class_setup_fields (klass); - g_assert (m_class_is_fields_inited (klass)); - - *out_value = NULL; - MonoClassField *klass_fields = m_class_get_fields (klass); - gpointer addr_for_has_value = mono_vtype_get_field_addr (nullable, &klass_fields[0]); - if (0 == *(guint8*)addr_for_has_value) - return FALSE; - - *out_value = mono_vtype_get_field_addr (nullable, &klass_fields[1]); - return TRUE; -} - -static gboolean -describe_value(MonoType * type, gpointer addr, int gpflags) -{ - ERROR_DECL (error); - switch (type->type) { - case MONO_TYPE_BOOLEAN: - mono_wasm_add_typed_value ("bool", NULL, *(gint8*)addr); - break; - case MONO_TYPE_I1: - mono_wasm_add_typed_value ("number", NULL, *(gint8*)addr); - break; - case MONO_TYPE_U1: - mono_wasm_add_typed_value ("number", NULL, *(guint8*)addr); - break; - case MONO_TYPE_CHAR: - mono_wasm_add_typed_value ("char", NULL, *(guint16*)addr); - break; - case MONO_TYPE_U2: - mono_wasm_add_typed_value ("number", NULL, *(guint16*)addr); - break; - case MONO_TYPE_I2: - mono_wasm_add_typed_value ("number", NULL, *(gint16*)addr); - break; - case MONO_TYPE_I4: - case MONO_TYPE_I: - mono_wasm_add_typed_value ("number", NULL, *(gint32*)addr); - break; - case MONO_TYPE_U4: - case MONO_TYPE_U: - mono_wasm_add_typed_value ("number", NULL, *(guint32*)addr); - break; - case MONO_TYPE_I8: - mono_wasm_add_typed_value ("number", NULL, *(gint64*)addr); - break; - case MONO_TYPE_U8: - mono_wasm_add_typed_value ("number", NULL, *(guint64*)addr); - break; - case MONO_TYPE_R4: - mono_wasm_add_typed_value ("number", NULL, *(float*)addr); - break; - case MONO_TYPE_R8: - mono_wasm_add_typed_value ("number", NULL, *(double*)addr); - break; - case MONO_TYPE_PTR: - case MONO_TYPE_FNPTR: { - char *class_name = mono_type_full_name (type); - const void *val = *(const void **)addr; - char *descr = g_strdup_printf ("(%s) %p", class_name, val); - - EM_ASM ({ - MONO.mono_wasm_add_typed_value ('pointer', $0, { ptr_addr: $1, klass_addr: $2 }); - }, descr, val ? addr : 0, val ? mono_class_from_mono_type_internal (type) : 0); - - g_free (descr); - g_free (class_name); - break; - } - - case MONO_TYPE_STRING: { - MonoString *str_obj = *(MonoString **)addr; - if (!str_obj) { - mono_wasm_add_typed_value ("string", NULL, 0); - } else { - char *str = mono_string_to_utf8_checked_internal (str_obj, error); - mono_error_assert_ok (error); /* FIXME report error */ - mono_wasm_add_typed_value ("string", str, 0); - g_free (str); - } - break; - } - - case MONO_TYPE_OBJECT: { - MonoObject *obj = *(MonoObject**)addr; - if (!obj) { - mono_wasm_add_obj_var ("object", NULL, 0); - break; - } - MonoClass *klass = obj->vtable->klass; - if (!klass) { - // boxed null - mono_wasm_add_obj_var ("object", NULL, 0); - break; - } - - type = m_class_get_byval_arg (klass); - if (type->type == MONO_TYPE_OBJECT) { - mono_wasm_add_obj_var ("object", "object", get_object_id (obj)); - break; - } - - // Boxed valuetype - if (m_class_is_valuetype (klass)) - addr = mono_object_unbox_internal (obj); - - return describe_value (type, addr, gpflags); - } - - case MONO_TYPE_GENERICINST: { - MonoClass *klass = mono_class_from_mono_type_internal (type); - if (mono_class_is_nullable (klass)) { - MonoType *targ = type->data.generic_class->context.class_inst->type_argv [0]; - - gpointer nullable_value = NULL; - if (nullable_try_get_value (addr, klass, &nullable_value)) { - return describe_value (targ, nullable_value, gpflags); - } else { - char* class_name = mono_type_full_name (type); - mono_wasm_add_obj_var (class_name, NULL, 0); - g_free (class_name); - break; - } - } - - if (mono_type_generic_inst_is_valuetype (type)) - goto handle_vtype; - /* - * else fallthrough - */ - } - - case MONO_TYPE_SZARRAY: - case MONO_TYPE_ARRAY: - case MONO_TYPE_CLASS: { - MonoObject *obj = *(MonoObject**)addr; - if (!obj) { - char *class_name = mono_type_full_name (type); - mono_wasm_add_func_var (class_name, NULL, 0); - g_free (class_name); - return TRUE; - } - MonoClass *klass = type->data.klass; - - if (m_class_is_valuetype (mono_object_class (obj))) { - addr = mono_object_unbox_internal (obj); - type = m_class_get_byval_arg (mono_object_class (obj)); - goto handle_vtype; - } - - char *class_name = mono_type_full_name (type); - int obj_id = get_object_id (obj); - - if (type-> type == MONO_TYPE_ARRAY || type->type == MONO_TYPE_SZARRAY) { - MonoArray *array = (MonoArray *)obj; - EM_ASM ({ - MONO.mono_wasm_add_typed_value ('array', $0, { objectId: $1, length: $2 }); - }, class_name, obj_id, mono_array_length_internal (array)); - } else if (m_class_is_delegate (klass) || (type->type == MONO_TYPE_GENERICINST && m_class_is_delegate (type->data.generic_class->container_class))) { - MonoMethod *method; - - if (type->type == MONO_TYPE_GENERICINST) - klass = type->data.generic_class->container_class; - - method = mono_get_delegate_invoke_internal (klass); - if (!method) { - mono_wasm_add_func_var (class_name, NULL, -1); - } else { - MonoMethod *tm = ((MonoDelegate *)obj)->method; - char *tm_desc = NULL; - if (tm) - tm_desc = mono_method_to_desc_for_js (tm, FALSE); - - mono_wasm_add_func_var (class_name, tm_desc, obj_id); - g_free (tm_desc); - } - } else { - char *to_string_val = get_to_string_description (class_name, klass, addr); - mono_wasm_add_obj_var (class_name, to_string_val, obj_id); - g_free (to_string_val); - } - g_free (class_name); - break; - } - - handle_vtype: - case MONO_TYPE_VALUETYPE: { - g_assert (addr); - MonoClass *klass = mono_class_from_mono_type_internal (type); - char *class_name = mono_type_full_name (type); - - if (m_class_is_enumtype (klass)) { - MonoClassField *field; - gpointer iter = NULL; - const char *p; - MonoTypeEnum def_type; - guint64 field_value; - guint64 value__ = 0xDEAD; - GString *enum_members = g_string_new (""); - int base_type = mono_class_enum_basetype_internal (klass)->type; - - while ((field = mono_class_get_fields_internal (klass, &iter))) { - if (strcmp ("value__", mono_field_get_name (field)) == 0) { - value__ = read_enum_value (mono_vtype_get_field_addr (addr, field), base_type); - continue; - } - - if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC)) - continue; - if (mono_field_is_deleted (field)) - continue; - - p = mono_class_get_field_default_value (field, &def_type); - /* this is to correctly increment `p` in the blob */ - /* len = */ mono_metadata_decode_blob_size (p, &p); - - field_value = read_enum_value (p, base_type); - - g_string_append_printf (enum_members, ",%s:%llu", mono_field_get_name (field), field_value); - } - - mono_wasm_add_enum_var (class_name, enum_members->str, value__); - g_string_free (enum_members, TRUE); - } else { - char *to_string_val = get_to_string_description (class_name, klass, addr); - - if (gpflags & GPFLAG_EXPAND_VALUETYPES) { - int32_t size = mono_class_value_size (klass, NULL); - void *value_buf = g_malloc0 (size); - mono_value_copy_internal (value_buf, addr, klass); - - EM_ASM ({ - MONO.mono_wasm_add_typed_value ($0, $1, { toString: $2, value_addr: $3, value_size: $4, klass: $5 }); - }, "begin_vt", class_name, to_string_val, value_buf, size, klass); - - g_free (value_buf); - - // FIXME: isAsyncLocalThis - describe_object_properties_for_klass (addr, klass, FALSE, gpflags); - mono_wasm_add_typed_value ("end_vt", NULL, 0); - } else { - EM_ASM ({ - MONO.mono_wasm_add_typed_value ($0, $1, { toString: $2 }); - }, "unexpanded_vt", class_name, to_string_val); - } - g_free (to_string_val); - } - g_free (class_name); - break; - } - default: { - char *type_name = mono_type_full_name (type); - char *msg = g_strdup_printf("can't handle type %s [%p, %x]", type_name, type, type->type); - mono_wasm_add_typed_value ("string", msg, 0); - g_free (msg); - g_free (type_name); - } - } - return TRUE; -} - -static gboolean -are_getters_allowed (const char *class_name) -{ - for (int i = 0; i < G_N_ELEMENTS (all_getters_allowed_class_names); i ++) { - if (strcmp (class_name, all_getters_allowed_class_names [i]) == 0) - return TRUE; - } - - return FALSE; -} - -static gboolean -invoke_and_describe_getter_value (MonoObject *obj, MonoProperty *p) -{ - ERROR_DECL (error); - MonoObject *res; - MonoObject *exc; - - MonoMethodSignature *sig = mono_method_signature_internal (p->get); - - res = mono_runtime_try_invoke_internal (p->get, obj, NULL, &exc, error); - if (!is_ok (error) && exc == NULL) - exc = (MonoObject *) mono_error_convert_to_exception (error); - if (exc) - { - const char *class_name = mono_class_full_name (mono_object_class (exc)); - ERROR_DECL (local_error); - char *str = mono_string_to_utf8_checked_internal (((MonoException*)exc)->message, local_error); - mono_error_assert_ok (local_error); /* FIXME report error */ - char *msg = g_strdup_printf("%s: %s", class_name, str); - mono_wasm_add_typed_value ("string", msg, 0); - g_free (msg); - return TRUE; - } - else if (!res || !m_class_is_valuetype (mono_object_class (res))) - return describe_value (sig->ret, &res, GPFLAG_EXPAND_VALUETYPES); - else - return describe_value (sig->ret, mono_object_unbox_internal (res), GPFLAG_EXPAND_VALUETYPES); -} - -static MonoObject* mono_runtime_try_invoke_internal (MonoMethod *method, void *obj, void **params, MonoObject **exc, MonoError* error) -{ - exception_on_runtime_invoke = NULL; - MonoObject* res = mono_runtime_try_invoke (method, obj, params, exc, error); - if (exception_on_runtime_invoke != NULL) - *exc = exception_on_runtime_invoke; - exception_on_runtime_invoke = NULL; - return res; -} - -static void -describe_object_properties_for_klass (void *obj, MonoClass *klass, gboolean isAsyncLocalThis, int gpflags) -{ - MonoClassField *f; - MonoProperty *p; - MonoMethodSignature *sig; - gboolean is_valuetype; - int pnum; - char *klass_name; - gboolean auto_invoke_getters; - gboolean is_own; - gboolean only_backing_fields; - - g_assert (klass); - MonoClass *start_klass = klass; - - only_backing_fields = gpflags & GPFLAG_ACCESSORS_ONLY; - is_valuetype = m_class_is_valuetype(klass); - if (is_valuetype) - gpflags |= GPFLAG_EXPAND_VALUETYPES; - -handle_parent: - is_own = (start_klass == klass); - klass_name = mono_class_full_name (klass); - gpointer iter = NULL; - while (obj && (f = mono_class_get_fields_internal (klass, &iter))) { - if (isAsyncLocalThis && f->name[0] == '<' && f->name[1] == '>') { - if (g_str_has_suffix (f->name, "__this")) { - mono_wasm_add_properties_var ("this", f->offset); - gpointer field_value = (guint8*)obj + f->offset; - - describe_value (f->type, field_value, gpflags); - } - - continue; - } - if (f->type->attrs & FIELD_ATTRIBUTE_STATIC) - continue; - if (mono_field_is_deleted (f)) - continue; - - if (only_backing_fields && !g_str_has_suffix(f->name, "k__BackingField")) - continue; - - EM_ASM ({ - MONO.mono_wasm_add_properties_var ($0, { field_offset: $1, is_own: $2, attr: $3, owner_class: $4 }); - }, f->name, f->offset, is_own, f->type->attrs, klass_name); - - gpointer field_addr; - if (is_valuetype) - field_addr = mono_vtype_get_field_addr (obj, f); - else - field_addr = (guint8*)obj + f->offset; - - describe_value (f->type, field_addr, gpflags); - } - - auto_invoke_getters = are_getters_allowed (klass_name); - iter = NULL; - pnum = 0; - while ((p = mono_class_get_properties (klass, &iter))) { - if (p->get->name) { //if get doesn't have name means that doesn't have a getter implemented and we don't want to show value, like VS debug - if (isAsyncLocalThis && (p->name[0] != '<' || (p->name[0] == '<' && p->name[1] == '>'))) - continue; - - sig = mono_method_signature_internal (p->get); - if (sig->param_count != 0) { - // getters with params are not shown - continue; - } - - if (p->get->flags & METHOD_ATTRIBUTE_STATIC) - continue; - - EM_ASM ({ - MONO.mono_wasm_add_properties_var ($0, { field_offset: $1, is_own: $2, attr: $3, owner_class: $4 }); - }, p->name, pnum, is_own, p->attrs, klass_name); - - gboolean vt_self_type_getter = is_valuetype && mono_class_from_mono_type_internal (sig->ret) == klass; - if (auto_invoke_getters && !vt_self_type_getter) { - invoke_and_describe_getter_value (obj, p); - } else { - // not allowed to call the getter here - char *ret_class_name = mono_class_full_name (mono_class_from_mono_type_internal (sig->ret)); - - mono_wasm_add_typed_value ("getter", ret_class_name, -1); - - g_free (ret_class_name); - continue; - } - } - pnum ++; - } - - g_free (klass_name); - - // ownProperties - // Note: ownProperties should mean that we return members of the klass itself, - // but we are going to ignore that here, because otherwise vscode/chrome don't - // seem to ask for inherited fields at all. - // if (!is_valuetype && !(gpflags & GPFLAG_OWN_PROPERTIES) && (klass = m_class_get_parent (klass))) - if (!is_valuetype && (klass = m_class_get_parent (klass))) - goto handle_parent; -} - -/* - * We return a `Target` property only for now. - * In future, we could add a `MethodInfo` too. - */ -static gboolean -describe_delegate_properties (MonoObject *obj) -{ - MonoClass *klass = mono_object_class(obj); - if (!m_class_is_delegate (klass)) - return FALSE; - - // Target, like in VS - what is this field supposed to be, anyway?? - MonoMethod *tm = ((MonoDelegate *)obj)->method; - char * sig_desc = mono_method_to_desc_for_js (tm, FALSE); - - mono_wasm_add_properties_var ("Target", -1); - mono_wasm_add_func_var (NULL, sig_desc, -1); - - g_free (sig_desc); - return TRUE; -} - -static gboolean -describe_object_properties (guint64 objectId, gboolean isAsyncLocalThis, int gpflags) -{ - PRINT_DEBUG_MSG (2, "describe_object_properties %llu, gpflags: %d\n", objectId, gpflags); - - MonoObject *obj = get_object_from_id (objectId); - if (!obj) - return FALSE; - - if (m_class_is_delegate (mono_object_class (obj))) { - // delegates get the same id format as regular objects - describe_delegate_properties (obj); - } else { - describe_object_properties_for_klass (obj, obj->vtable->klass, isAsyncLocalThis, gpflags); - } - - return TRUE; -} - -static gboolean -invoke_getter (void *obj_or_value, MonoClass *klass, const char *name) -{ - if (!obj_or_value || !klass || !name) { - PRINT_DEBUG_MSG (2, "invoke_getter: none of the arguments can be null"); - return FALSE; - } - - gpointer iter; -handle_parent: - iter = NULL; - MonoProperty *p; - while ((p = mono_class_get_properties (klass, &iter))) { - //if get doesn't have name means that doesn't have a getter implemented and we don't want to show value, like VS debug - if (!p->get->name || strcasecmp (p->name, name) != 0) - continue; - - invoke_and_describe_getter_value (obj_or_value, p); - return TRUE; - } - - if ((klass = m_class_get_parent(klass))) - goto handle_parent; - - return FALSE; -} - -static gboolean -describe_array_values (guint64 objectId, int startIdx, int count, int gpflags) -{ - if (count == 0) - return TRUE; - - int esize; - gpointer elem; - MonoArray *arr = (MonoArray*) get_object_from_id (objectId); - if (!arr) - return FALSE; - - MonoClass *klass = mono_object_class (arr); - MonoTypeEnum type = m_class_get_byval_arg (klass)->type; - if (type != MONO_TYPE_SZARRAY && type != MONO_TYPE_ARRAY) { - PRINT_DEBUG_MSG (1, "describe_array_values: object is not an array. type: 0x%x\n", type); - return FALSE; - } - - int len = arr->max_length; - if (len == 0 && startIdx == 0 && count <= 0) { - // Nothing to do - return TRUE; - } - - if (startIdx < 0 || (len > 0 && startIdx >= len)) { - PRINT_DEBUG_MSG (1, "describe_array_values: invalid startIdx (%d) for array of length %d\n", startIdx, len); - return FALSE; - } - - if (count > 0 && (startIdx + count) > len) { - PRINT_DEBUG_MSG (1, "describe_array_values: invalid count (%d) for startIdx: %d, and array of length %d\n", count, startIdx, len); - return FALSE; - } - - esize = mono_array_element_size (klass); - int endIdx = count < 0 ? len : startIdx + count; - - for (int i = startIdx; i < endIdx; i ++) { - mono_wasm_add_array_item(i); - elem = (gpointer*)((char*)arr->vector + (i * esize)); - describe_value (m_class_get_byval_arg (m_class_get_element_class (klass)), elem, gpflags); - } - return TRUE; -} - -static void -describe_async_method_locals (InterpFrame *frame, MonoMethod *method) -{ - //Async methods are special in the way that local variables can be lifted to generated class fields - gpointer addr = NULL; - if (mono_debug_lookup_method_async_debug_info (method)) { - addr = mini_get_interp_callbacks ()->frame_get_this (frame); - MonoObject *obj = *(MonoObject**)addr; - int objId = get_object_id (obj); - mono_wasm_set_is_async_method (objId); - describe_object_properties (objId, TRUE, GPFLAG_NONE); - } -} - -static void -describe_non_async_this (InterpFrame *frame, MonoMethod *method) -{ - gpointer addr = NULL; - if (mono_debug_lookup_method_async_debug_info (method)) - return; - - if (mono_method_signature_internal (method)->hasthis) { - addr = mini_get_interp_callbacks ()->frame_get_this (frame); - MonoObject *obj = *(MonoObject**)addr; - MonoClass *klass = method->klass; - MonoType *type = m_class_get_byval_arg (method->klass); - - mono_wasm_add_properties_var ("this", -1); - - if (m_class_is_valuetype (klass)) { - describe_value (type, obj, GPFLAG_EXPAND_VALUETYPES); - } else { - // this is an object, and we can retrieve the valuetypes in it later - // through the object id - describe_value (type, addr, GPFLAG_NONE); - } - } -} - -static gboolean -describe_variable (InterpFrame *frame, MonoMethod *method, MonoMethodHeader *header, int pos, int gpflags) -{ - MonoType *type = NULL; - gpointer addr = NULL; - if (pos < 0) { - MonoMethodSignature *sig = mono_method_signature_internal (method); - pos = -pos - 1; - - if (pos >= sig->param_count) { - PRINT_DEBUG_MSG(1, "BUG: describe_variable, trying to access param indexed %d, but the method (%s) has only %d params\n", pos, method->name, sig->param_count); - return FALSE; - } - - type = sig->params [pos]; - addr = mini_get_interp_callbacks ()->frame_get_arg (frame, pos); - } else { - if (pos >= header->num_locals) { - PRINT_DEBUG_MSG(1, "BUG: describe_variable, trying to access local indexed %d, but the method (%s) has only %d locals\n", pos, method->name, header->num_locals); - return FALSE; - } - - type = header->locals [pos]; - addr = mini_get_interp_callbacks ()->frame_get_local (frame, pos); - } - - PRINT_DEBUG_MSG (2, "adding val %p type 0x%x %s\n", addr, type->type, mono_type_full_name (type)); - - return describe_value(type, addr, gpflags); -} - -static gboolean -decode_value (MonoType *t, guint8 *addr, const char* variableValue) -{ - char* endptr; - errno = 0; - switch (t->type) { - case MONO_TYPE_BOOLEAN: - if (!strcasecmp (variableValue, "True")) - *(guint8*)addr = 1; - else if (!strcasecmp (variableValue, "False")) - *(guint8*)addr = 0; - else - return FALSE; - break; - case MONO_TYPE_CHAR: - if (strlen (variableValue) > 1) - return FALSE; - *(gunichar2*)addr = variableValue [0]; - break; - case MONO_TYPE_I1: { - intmax_t val = strtoimax (variableValue, &endptr, 10); - if (errno != 0) - return FALSE; - if (val >= -128 && val <= 127) - *(gint8*)addr = val; - else - return FALSE; - break; - } - case MONO_TYPE_U1: { - intmax_t val = strtoimax (variableValue, &endptr, 10); - if (errno != 0) - return FALSE; - if (val >= 0 && val <= 255) - *(guint8*)addr = val; - else - return FALSE; - break; - } - case MONO_TYPE_I2: { - intmax_t val = strtoimax (variableValue, &endptr, 10); - if (errno != 0) - return FALSE; - if (val >= -32768 && val <= 32767) - *(gint16*)addr = val; - else - return FALSE; - break; - } - case MONO_TYPE_U2: { - intmax_t val = strtoimax (variableValue, &endptr, 10); - if (errno != 0) - return FALSE; - if (val >= 0 && val <= 65535) - *(guint16*)addr = val; - else - return FALSE; - break; - } - case MONO_TYPE_I4: { - intmax_t val = strtoimax (variableValue, &endptr, 10); - if (errno != 0) - return FALSE; - if (val >= -2147483648 && val <= 2147483647) - *(gint32*)addr = val; - else - return FALSE; - break; - } - case MONO_TYPE_U4: { - intmax_t val = strtoimax (variableValue, &endptr, 10); - if (errno != 0) - return FALSE; - if (val >= 0 && val <= 4294967295) - *(guint32*)addr = val; - else - return FALSE; - break; - } - case MONO_TYPE_I8: { - long long val = strtoll (variableValue, &endptr, 10); - if (errno != 0) - return FALSE; - *(gint64*)addr = val; - break; - } - case MONO_TYPE_U8: { - long long val = strtoll (variableValue, &endptr, 10); - if (errno != 0) - return FALSE; - *(guint64*)addr = val; - break; - } - case MONO_TYPE_R4: { - gfloat val = strtof (variableValue, &endptr); - if (errno != 0) - return FALSE; - *(gfloat*)addr = val; - break; - } - case MONO_TYPE_R8: { - gdouble val = strtof (variableValue, &endptr); - if (errno != 0) - return FALSE; - *(gdouble*)addr = val; - break; - } - default: - return FALSE; - } - return TRUE; -} - -static gboolean -set_variable_value_on_frame (MonoStackFrameInfo *info, MonoContext *ctx, gpointer ud) -{ - ERROR_DECL (error); - SetVariableValueData *data = (SetVariableValueData*)ud; - gboolean is_arg = FALSE; - MonoType *t = NULL; - guint8 *val_buf = NULL; - - ++data->cur_frame; - - //skip wrappers - if (info->type != FRAME_TYPE_MANAGED && info->type != FRAME_TYPE_INTERP) { - return FALSE; - } - - if (data->cur_frame != data->target_frame) - return FALSE; - - data->found = TRUE; - - InterpFrame *frame = (InterpFrame*)info->interp_frame; - MonoMethod *method = frame->imethod->method; - MonoMethodSignature *sig = mono_method_signature_internal (method); - MonoMethodHeader *header = mono_method_get_header_checked (method, error); - - if (!header) { - mono_error_cleanup(error); - data->error = TRUE; - return TRUE; - } - - if (!sig) - goto exit_with_error; - - int pos = data->pos; - - if (pos < 0) { - pos = - pos - 1; - if (pos >= sig->param_count) - goto exit_with_error; - is_arg = TRUE; - t = sig->params [pos]; - } - else { - if (pos >= header->num_locals) - goto exit_with_error; - t = header->locals [pos]; - } - - guint8 *addr; - if (is_arg) - addr = (guint8*)mini_get_interp_callbacks ()->frame_get_arg (frame, pos); - else - addr = (guint8*)mini_get_interp_callbacks ()->frame_get_local (frame, pos); - - val_buf = (guint8 *)g_alloca (mono_class_instance_size (mono_class_from_mono_type_internal (t))); - - if (!decode_value(t, val_buf, data->new_value)) - goto exit_with_error; - - DbgEngineErrorCode errorCode = mono_de_set_interp_var (t, addr, val_buf); - if (errorCode != ERR_NONE) { - goto exit_with_error; - } - - mono_metadata_free_mh (header); - return TRUE; - -exit_with_error: - data->error = TRUE; - mono_metadata_free_mh (header); - return TRUE; -} - -static gboolean -describe_variables_on_frame (MonoStackFrameInfo *info, MonoContext *ctx, gpointer ud) -{ - ERROR_DECL (error); - FrameDescData *data = (FrameDescData*)ud; - - ++data->cur_frame; - - //skip wrappers - if (info->type != FRAME_TYPE_MANAGED && info->type != FRAME_TYPE_INTERP) { - return FALSE; - } - - if (data->cur_frame != data->target_frame) - return FALSE; - - data->found = TRUE; - - InterpFrame *frame = (InterpFrame*)info->interp_frame; - g_assert (frame); - MonoMethod *method = frame->imethod->method; - g_assert (method); - - MonoMethodHeader *header = mono_method_get_header_checked (method, error); - mono_error_assert_ok (error); /* FIXME report error */ - - for (int i = 0; i < data->len; i++) - { - if (!describe_variable (frame, method, header, data->pos[i], GPFLAG_EXPAND_VALUETYPES)) - mono_wasm_add_typed_value("symbol", "", 0); - } - - describe_async_method_locals (frame, method); - describe_non_async_this (frame, method); - - mono_metadata_free_mh (header); - return TRUE; -} - -EMSCRIPTEN_KEEPALIVE gboolean -mono_wasm_set_variable_on_frame (int scope, int index, const char* name, const char* value) -{ - if (scope < 0) - return FALSE; - - SetVariableValueData data; - data.target_frame = scope; - data.cur_frame = -1; - data.pos = index; - data.found = FALSE; - data.new_value = value; - data.error = FALSE; - - mono_walk_stack_with_ctx (set_variable_value_on_frame, NULL, MONO_UNWIND_NONE, &data); - return !data.error; -} - -EMSCRIPTEN_KEEPALIVE gboolean -mono_wasm_get_deref_ptr_value (void *value_addr, MonoClass *klass) -{ - MonoType *type = m_class_get_byval_arg (klass); - if (type->type != MONO_TYPE_PTR && type->type != MONO_TYPE_FNPTR) { - PRINT_DEBUG_MSG (2, "BUG: mono_wasm_get_deref_ptr_value: Expected to get a ptr type, but got 0x%x\n", type->type); - return FALSE; - } - - mono_wasm_add_properties_var ("deref", -1); - return describe_value (type->data.type, value_addr, GPFLAG_EXPAND_VALUETYPES); -} - -//FIXME this doesn't support getting the return value pseudo-var -EMSCRIPTEN_KEEPALIVE gboolean -mono_wasm_get_local_vars (int scope, int* pos, int len) -{ - if (scope < 0) - return FALSE; - - FrameDescData data; - data.target_frame = scope; - data.cur_frame = -1; - data.len = len; - data.pos = pos; - data.found = FALSE; - - mono_walk_stack_with_ctx (describe_variables_on_frame, NULL, MONO_UNWIND_NONE, &data); - - return data.found; -} - -EMSCRIPTEN_KEEPALIVE gboolean -mono_wasm_get_object_properties (int object_id, int gpflags) -{ - PRINT_DEBUG_MSG (2, "getting properties of object %d, gpflags: %d\n", object_id, gpflags); - - return describe_object_properties (object_id, FALSE, gpflags); -} - -EMSCRIPTEN_KEEPALIVE gboolean -mono_wasm_get_array_values (int object_id, int start_idx, int count, int gpflags) -{ - PRINT_DEBUG_MSG (2, "getting array values %d, startIdx: %d, count: %d, gpflags: 0x%x\n", object_id, start_idx, count, gpflags); - - return describe_array_values (object_id, start_idx, count, gpflags); -} - -EMSCRIPTEN_KEEPALIVE gboolean -mono_wasm_invoke_getter_on_object (int object_id, const char* name) -{ - MonoObject *obj = get_object_from_id (object_id); - if (!obj) - return FALSE; - - return invoke_getter (obj, mono_object_class (obj), name); -} - -EMSCRIPTEN_KEEPALIVE gboolean -mono_wasm_set_value_on_object (int object_id, const char* name, const char* value) -{ - PRINT_DEBUG_MSG (1, "mono_wasm_set_value_on_object %d, name: %s, value: %s\n", object_id, name, value); - MonoObject *obj = get_object_from_id (object_id); - - if (!obj || !name) { - PRINT_DEBUG_MSG (2, "mono_wasm_set_value_on_object: none of the arguments can be null"); - return FALSE; - } - MonoClass* klass = mono_object_class (obj); - - gpointer iter; -handle_parent: - iter = NULL; - MonoClassField *f; - while ((f = mono_class_get_fields_internal (klass, &iter))) { - if (!f->name || strcasecmp (f->name, name) != 0) - continue; - guint8 *val_buf = (guint8 *)g_alloca (mono_class_instance_size (mono_class_from_mono_type_internal (f->type))); - - if (!decode_value(f->type, val_buf, value)) { - return FALSE; - } - DbgEngineErrorCode errorCode = mono_de_set_interp_var (f->type, (guint8*)obj + f->offset, val_buf); - if (errorCode != ERR_NONE) { - return FALSE; - } - return TRUE; - } - - iter = NULL; - MonoProperty *p; - MonoObject *exc; - ERROR_DECL (error); - while ((p = mono_class_get_properties (klass, &iter))) { - if (!p->name || strcasecmp (p->name, name) != 0) - continue; - if (!p->set) - break; - MonoType *type = mono_method_signature_internal (p->set)->params [0]; - guint8 *val_buf = (guint8 *)g_alloca (mono_class_instance_size (mono_class_from_mono_type_internal (type))); - - if (!decode_value(type, val_buf, value)) { - return FALSE; - } - mono_runtime_try_invoke (p->set, obj, (void **)&val_buf, &exc, error); - if (!is_ok (error) && exc == NULL) - exc = (MonoObject*) mono_error_convert_to_exception (error); - if (exc) { - char *error_message = mono_string_to_utf8_checked_internal (((MonoException *)exc)->message, error); - if (is_ok (error)) { - PRINT_DEBUG_MSG (2, "mono_wasm_set_value_on_object exception: %s\n", error_message); - g_free (error_message); - mono_error_cleanup (error); - } - else { - PRINT_DEBUG_MSG (2, "mono_wasm_set_value_on_object exception\n"); - } - return FALSE; - } - return TRUE; - } - - if ((klass = m_class_get_parent(klass))) - goto handle_parent; + EM_ASM ({ + MONO.mono_wasm_add_dbg_command_received (1, -1, $0, $1); + }, data, len); + mono_wasm_save_thread_context(); + mono_wasm_fire_debugger_agent_message (); return FALSE; } -EMSCRIPTEN_KEEPALIVE gboolean -mono_wasm_invoke_getter_on_value (void *value, MonoClass *klass, const char *name) -{ - PRINT_DEBUG_MSG (2, "mono_wasm_invoke_getter_on_value: v: %p klass: %p, name: %s\n", value, klass, name); - if (!klass || !value) - return FALSE; - - if (!m_class_is_valuetype (klass)) { - PRINT_DEBUG_MSG (2, "mono_wasm_invoke_getter_on_value: klass is not a valuetype. name: %s\n", mono_class_full_name (klass)); - return FALSE; - } - - return invoke_getter (value, klass, name); -} - -EMSCRIPTEN_KEEPALIVE void -mono_wasm_set_is_debugger_attached (gboolean is_attached) -{ - mono_set_is_debugger_attached (is_attached); - if (is_attached && has_pending_lazy_loaded_assemblies) - { - GPtrArray *assemblies = mono_alc_get_all_loaded_assemblies (); - for (int i = 0; i < assemblies->len; ++i) { - MonoAssembly *ass = (MonoAssembly*)g_ptr_array_index (assemblies, i); - assembly_loaded (NULL, ass); - } - g_ptr_array_free (assemblies, TRUE); - has_pending_lazy_loaded_assemblies = FALSE; - } -} - -// Functions required by debugger-state-machine. -gsize -mono_debugger_tls_thread_id (DebuggerTlsData *debuggerTlsData) -{ - return 1; -} - #else // HOST_WASM void diff --git a/src/mono/sample/wasm/browser/Wasm.Browser.Sample.csproj b/src/mono/sample/wasm/browser/Wasm.Browser.Sample.csproj index 2b05836627673..15386dece90a9 100644 --- a/src/mono/sample/wasm/browser/Wasm.Browser.Sample.csproj +++ b/src/mono/sample/wasm/browser/Wasm.Browser.Sample.csproj @@ -1,7 +1,11 @@ + Debug true runtime.js + true + embedded + 1 diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs index 1f31e044653ab..81c419e4c5850 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs @@ -317,6 +317,7 @@ internal class MethodInfo public SourceId SourceId => source.SourceId; + public int DebuggerId { get; set; } public string Name { get; } public MethodDebugInformation DebugInformation; public MethodDefinitionHandle methodDefHandle; @@ -325,7 +326,7 @@ internal class MethodInfo public SourceLocation EndLocation { get; } public AssemblyInfo Assembly { get; } public int Token { get; } - + public bool IsStatic() => (methodDef.Attributes & MethodAttributes.Static) != 0; public MethodInfo(AssemblyInfo assembly, MethodDefinitionHandle methodDefHandle, int token, SourceFile source, TypeInfo type) { this.Assembly = assembly; diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs index efb2a7b4a2a79..f39f2736caaeb 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs @@ -172,50 +172,33 @@ internal class MonoCommands public MonoCommands(string expression) => this.expression = expression; - public static MonoCommands GetCallStack() => new MonoCommands("MONO.mono_wasm_get_call_stack()"); - public static MonoCommands GetExceptionObject() => new MonoCommands("MONO.mono_wasm_get_exception_object()"); - public static MonoCommands IsRuntimeReady() => new MonoCommands("MONO.mono_wasm_runtime_is_ready"); + public static MonoCommands GetDebuggerAgentBufferReceived() => new MonoCommands("MONO.mono_wasm_get_dbg_command_info()"); - public static MonoCommands StartSingleStepping(StepKind kind) => new MonoCommands($"MONO.mono_wasm_start_single_stepping ({(int)kind})"); + public static MonoCommands IsRuntimeReady() => new MonoCommands("MONO.mono_wasm_runtime_is_ready"); public static MonoCommands GetLoadedFiles() => new MonoCommands("MONO.mono_wasm_get_loaded_files()"); - public static MonoCommands ClearAllBreakpoints() => new MonoCommands("MONO.mono_wasm_clear_all_breakpoints()"); - - public static MonoCommands GetDetails(DotnetObjectId objectId, JToken args = null) => new MonoCommands($"MONO.mono_wasm_get_details ('{objectId}', {(args ?? "{ }")})"); - - public static MonoCommands GetScopeVariables(int scopeId, params VarInfo[] vars) - { - var var_ids = vars.Select(v => new { index = v.Index, name = v.Name }).ToArray(); - return new MonoCommands($"MONO.mono_wasm_get_variables({scopeId}, {JsonConvert.SerializeObject(var_ids)})"); - } - - public static MonoCommands SetVariableValue(int scopeId, int index, string name, string newValue) + public static MonoCommands SendDebuggerAgentCommand(int id, int command_set, int command, string command_parameters) { - return new MonoCommands($"MONO.mono_wasm_set_variable_value({scopeId}, {index}, '{name}', '{newValue}')"); + return new MonoCommands($"MONO.mono_wasm_send_dbg_command ({id}, {command_set}, {command},'{command_parameters}')"); } - public static MonoCommands EvaluateMemberAccess(int scopeId, string expr, params VarInfo[] vars) + public static MonoCommands SendDebuggerAgentCommandWithParms(int id, int command_set, int command, string command_parameters, int len, int type, string parm) { - var var_ids = vars.Select(v => new { index = v.Index, name = v.Name }).ToArray(); - return new MonoCommands($"MONO.mono_wasm_eval_member_access({scopeId}, {JsonConvert.SerializeObject(var_ids)}, '', '{expr}')"); + return new MonoCommands($"MONO.mono_wasm_send_dbg_command_with_parms ({id}, {command_set}, {command},'{command_parameters}', {len}, {type}, '{parm}')"); } - public static MonoCommands SetBreakpoint(string assemblyName, int methodToken, int ilOffset) => new MonoCommands($"MONO.mono_wasm_set_breakpoint (\"{assemblyName}\", {methodToken}, {ilOffset})"); - - public static MonoCommands RemoveBreakpoint(int breakpointId) => new MonoCommands($"MONO.mono_wasm_remove_breakpoint({breakpointId})"); - - public static MonoCommands ReleaseObject(DotnetObjectId objectId) => new MonoCommands($"MONO.mono_wasm_release_object('{objectId}')"); - public static MonoCommands CallFunctionOn(JToken args) => new MonoCommands($"MONO.mono_wasm_call_function_on ({args.ToString()})"); - public static MonoCommands Resume() => new MonoCommands($"MONO.mono_wasm_debugger_resume ()"); + public static MonoCommands GetDetails(int objectId, JToken args = null) => new MonoCommands($"MONO.mono_wasm_get_details ({objectId}, {(args ?? "{ }")})"); - public static MonoCommands SetPauseOnExceptions(string state) => new MonoCommands($"MONO.mono_wasm_set_pause_on_exceptions(\"{state}\")"); + public static MonoCommands Resume() => new MonoCommands($"MONO.mono_wasm_debugger_resume ()"); public static MonoCommands DetachDebugger() => new MonoCommands($"MONO.mono_wasm_detach_debugger()"); + + public static MonoCommands ReleaseObject(DotnetObjectId objectId) => new MonoCommands($"MONO.mono_wasm_release_object('{objectId}')"); } internal enum MonoErrorCodes @@ -280,8 +263,8 @@ internal enum BreakpointState internal enum StepKind { Into, - Out, - Over + Over, + Out } internal class ExecutionContext @@ -292,6 +275,7 @@ internal class ExecutionContext public TaskCompletionSource ready; public bool IsRuntimeReady => ready != null && ready.Task.IsCompleted; + public int ThreadId { get; set; } public int Id { get; set; } public object AuxData { get; set; } diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MemberReferenceResolver.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MemberReferenceResolver.cs index 21f8dabdb37a6..c4e578a0a2c92 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MemberReferenceResolver.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MemberReferenceResolver.cs @@ -8,6 +8,7 @@ using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using System.IO; namespace Microsoft.WebAssembly.Diagnostics { @@ -18,7 +19,6 @@ internal class MemberReferenceResolver private MonoProxy proxy; private ExecutionContext ctx; private PerScopeCache scopeCache; - private VarInfo[] varIds; private ILogger logger; private bool locals_fetched; @@ -31,44 +31,99 @@ public MemberReferenceResolver(MonoProxy proxy, ExecutionContext ctx, SessionId this.logger = logger; scopeCache = ctx.GetCacheForScope(scope_id); } - - // Checks Locals, followed by `this` - public async Task Resolve(string var_name, CancellationToken token) + public async Task GetValueFromObject(JToken objRet, CancellationToken token) { - if (scopeCache.Locals.Count == 0 && !locals_fetched) + if (objRet["value"]?["className"]?.Value() == "System.Exception") { - Result scope_res = await proxy.GetScopeProperties(sessionId, scopeId, token); - if (scope_res.IsErr) - throw new Exception($"BUG: Unable to get properties for scope: {scopeId}. {scope_res}"); - locals_fetched = true; + if (DotnetObjectId.TryParse(objRet?["value"]?["objectId"]?.Value(), out DotnetObjectId objectId)) + { + var exceptionObject = await proxy.sdbHelper.GetObjectValues(sessionId, int.Parse(objectId.Value), true, false, false, true, token); + var exceptionObjectMessage = exceptionObject.FirstOrDefault(attr => attr["name"].Value().Equals("_message")); + exceptionObjectMessage["value"]["value"] = objRet["value"]?["className"]?.Value() + ": " + exceptionObjectMessage["value"]?["value"]?.Value(); + return exceptionObjectMessage["value"]?.Value(); + } + return objRet["value"]?.Value(); } - if (scopeCache.Locals.TryGetValue(var_name, out JObject obj)) + if (objRet["value"]?.Value() != null) + return objRet["value"]?.Value(); + if (objRet["get"]?.Value() != null) { - return obj["value"]?.Value(); - } + if (DotnetObjectId.TryParse(objRet?["get"]?["objectIdValue"]?.Value(), out DotnetObjectId objectId)) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.WriteObj(objectId, proxy.sdbHelper); + var ret = await proxy.sdbHelper.InvokeMethod(sessionId, command_params.ToArray(), objRet["get"]["methodId"].Value(), objRet["name"].Value(), token); + return await GetValueFromObject(ret, token); + } - if (scopeCache.MemberReferences.TryGetValue(var_name, out JObject ret)) - return ret; - - if (varIds == null) - { - Frame scope = ctx.CallStack.FirstOrDefault(s => s.Id == scopeId); - varIds = scope.Method.GetLiveVarsAt(scope.Location.CliLocation.Offset); } + return null; + } + // Checks Locals, followed by `this` + public async Task Resolve(string var_name, CancellationToken token) + { + string[] parts = var_name.Split("."); + JObject rootObject = null; - Result res = await proxy.SendMonoCommand(sessionId, MonoCommands.EvaluateMemberAccess(scopeId, var_name, varIds), token); - if (res.IsOk) - { - ret = res.Value?["result"]?["value"]?["value"]?.Value(); - scopeCache.MemberReferences[var_name] = ret; + if (scopeCache.MemberReferences.TryGetValue(var_name, out JObject ret)) { + return ret; } - else + foreach (string part in parts) { - logger.LogDebug(res.Error.ToString()); - } + string partTrimmed = part.Trim(); + if (partTrimmed == "") + return null; + if (rootObject != null) + { + if (rootObject?["subtype"]?.Value() == "null") + return null; + if (DotnetObjectId.TryParse(rootObject?["objectId"]?.Value(), out DotnetObjectId objectId)) + { + var root_res_obj = await proxy.RuntimeGetPropertiesInternal(sessionId, objectId, null, token); + var objRet = root_res_obj.FirstOrDefault(objPropAttr => objPropAttr["name"].Value() == partTrimmed); + if (objRet == null) + return null; - return ret; + rootObject = await GetValueFromObject(objRet, token); + } + continue; + } + if (scopeCache.Locals.Count == 0 && !locals_fetched) + { + Result scope_res = await proxy.GetScopeProperties(sessionId, scopeId, token); + if (scope_res.IsErr) + throw new Exception($"BUG: Unable to get properties for scope: {scopeId}. {scope_res}"); + locals_fetched = true; + } + if (scopeCache.Locals.TryGetValue(partTrimmed, out JObject obj)) + { + rootObject = obj["value"]?.Value(); + } + else if (scopeCache.Locals.TryGetValue("this", out JObject objThis)) + { + if (partTrimmed == "this") + { + rootObject = objThis?["value"].Value(); + } + else if (DotnetObjectId.TryParse(objThis?["value"]?["objectId"]?.Value(), out DotnetObjectId objectId)) + { + var root_res_obj = await proxy.RuntimeGetPropertiesInternal(sessionId, objectId, null, token); + var objRet = root_res_obj.FirstOrDefault(objPropAttr => objPropAttr["name"].Value() == partTrimmed); + if (objRet != null) + { + rootObject = await GetValueFromObject(objRet, token); + } + else + { + return null; + } + } + } + } + scopeCache.MemberReferences[var_name] = rootObject; + return rootObject; } } diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs index b80a4483ca02d..689904740d3d6 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs @@ -18,6 +18,7 @@ namespace Microsoft.WebAssembly.Diagnostics { internal class MonoProxy : DevToolsProxy { + internal MonoSDBHelper sdbHelper; private IList urlSymbolServerList; private static HttpClient client = new HttpClient(); private HashSet sessions = new HashSet(); @@ -26,6 +27,7 @@ internal class MonoProxy : DevToolsProxy public MonoProxy(ILoggerFactory loggerFactory, IList urlSymbolServerList) : base(loggerFactory) { this.urlSymbolServerList = urlSymbolServerList ?? new List(); + sdbHelper = new MonoSDBHelper(this); } internal ExecutionContext GetContext(SessionId sessionId) @@ -132,11 +134,15 @@ protected override async Task AcceptEvent(SessionId sessionId, string meth await SendCommand(sessionId, "Debugger.resume", new JObject(), token); return true; } - case "mono_wasm_fire_bp": - case "_mono_wasm_fire_bp": - case "_mono_wasm_fire_exception": + case "_mono_wasm_fire_debugger_agent_message": { - return await OnPause(sessionId, args, token); + try { + return await OnReceiveDebuggerAgentEvent(sessionId, args, token); + } + catch (Exception) //if the page is refreshed maybe it stops here. + { + return false; + } } } break; @@ -370,8 +376,12 @@ protected override async Task AcceptCommand(MessageId id, string method, J if (!DotnetObjectId.TryParse(args?["objectId"], out DotnetObjectId objectId)) break; - Result result = await RuntimeGetProperties(id, objectId, args, token); - SendResponse(id, result, token); + var ret = await RuntimeGetPropertiesInternal(id, objectId, args, token); + if (ret == null) { + SendResponse(id, Result.Err($"Unable to RuntimeGetProperties '{objectId}'"), token); + } + else + SendResponse(id, Result.OkFromObject(new { result = ret }), token); return true; } @@ -388,7 +398,7 @@ protected override async Task AcceptCommand(MessageId id, string method, J case "Debugger.setPauseOnExceptions": { string state = args["state"].Value(); - await SendMonoCommand(id, MonoCommands.SetPauseOnExceptions(state), token); + await sdbHelper.EnableExceptions(id, state, token); // Pass this on to JS too return false; } @@ -403,8 +413,6 @@ protected override async Task AcceptCommand(MessageId id, string method, J } case "DotnetDebugger.getMethodLocation": { - Console.WriteLine("set-breakpoint-by-method: " + id + " " + args); - DebugStore store = await RuntimeReady(id, token); string aname = args["assemblyName"]?.Value(); string typeName = args["typeName"]?.Value(); @@ -460,31 +468,80 @@ protected override async Task AcceptCommand(MessageId id, string method, J } case "Runtime.callFunctionOn": { - if (!DotnetObjectId.TryParse(args["objectId"], out DotnetObjectId objectId)) - return false; - - if (objectId.Scheme == "scope") - { + try { + return await CallOnFunction(id, args, token); + } + catch (Exception){ SendResponse(id, Result.Exception(new ArgumentException( - $"Runtime.callFunctionOn not supported with scope ({objectId}).")), + $"Runtime.callFunctionOn not supported with ({args["objectId"]}).")), token); return true; } - - Result res = await SendMonoCommand(id, MonoCommands.CallFunctionOn(args), token); - JTokenType? res_value_type = res.Value?["result"]?["value"]?.Type; - - if (res.IsOk && res_value_type == JTokenType.Object || res_value_type == JTokenType.Object) - res = Result.OkFromObject(new { result = res.Value["result"]["value"] }); - - SendResponse(id, res, token); - return true; } } return false; } + private async Task CallOnFunction(MessageId id, JObject args, CancellationToken token) + { + if (!DotnetObjectId.TryParse(args["objectId"], out DotnetObjectId objectId)) { + return false; + } + switch (objectId.Scheme) + { + case "object": + args["details"] = await sdbHelper.GetObjectProxy(id, int.Parse(objectId.Value), token); + break; + case "valuetype": + args["details"] = await sdbHelper.GetValueTypeProxy(id, int.Parse(objectId.Value), token); + break; + case "pointer": + args["details"] = await sdbHelper.GetPointerContent(id, int.Parse(objectId.Value), token); + break; + case "array": + args["details"] = await sdbHelper.GetArrayValues(id, int.Parse(objectId.Value), token); + break; + case "cfo_res": + { + Result cfo_res = await SendMonoCommand(id, MonoCommands.CallFunctionOn(args), token); + cfo_res = Result.OkFromObject(new { result = cfo_res.Value?["result"]?["value"]}); + SendResponse(id, cfo_res, token); + return true; + } + case "scope": + { + SendResponse(id, + Result.Exception(new ArgumentException( + $"Runtime.callFunctionOn not supported with scope ({objectId}).")), + token); + return true; + } + default: + return false; + } + Result res = await SendMonoCommand(id, MonoCommands.CallFunctionOn(args), token); + if (res.IsErr) + { + SendResponse(id, res, token); + return true; + } + if (res.Value?["result"]?["value"]?["type"] == null) //it means that is not a buffer returned from the debugger-agent + { + byte[] newBytes = Convert.FromBase64String(res.Value?["result"]?["value"]?["value"]?.Value()); + var ret_debugger_cmd = new MemoryStream(newBytes); + var ret_debugger_cmd_reader = new MonoBinaryReader(ret_debugger_cmd); + ret_debugger_cmd_reader.ReadByte(); //number of objects returned. + var obj = await sdbHelper.CreateJObjectForVariableValue(id, ret_debugger_cmd_reader, "ret", false, -1, token); + /*JTokenType? res_value_type = res.Value?["result"]?["value"]?.Type;*/ + res = Result.OkFromObject(new { result = obj["value"]}); + SendResponse(id, res, token); + return true; + } + res = Result.OkFromObject(new { result = res.Value?["result"]?["value"]}); + SendResponse(id, res, token); + return true; + } private async Task OnSetVariableValue(MessageId id, int scopeId, string varName, JToken varValue, CancellationToken token) { @@ -498,50 +555,59 @@ private async Task OnSetVariableValue(MessageId id, int scopeId, string va var varToSetValue = varIds.FirstOrDefault(v => v.Name == varName); if (varToSetValue == null) return false; - Result res = await SendMonoCommand(id, MonoCommands.SetVariableValue(scopeId, varToSetValue.Index, varName, varValue["value"].Value()), token); - if (res.IsOk) + var res = await sdbHelper.SetVariableValue(id, ctx.ThreadId, scopeId, varToSetValue.Index, varValue["value"].Value(), token); + if (res) SendResponse(id, Result.Ok(new JObject()), token); else SendResponse(id, Result.Err($"Unable to set '{varValue["value"].Value()}' to variable '{varName}'"), token); return true; } - private async Task RuntimeGetProperties(MessageId id, DotnetObjectId objectId, JToken args, CancellationToken token) + internal async Task RuntimeGetPropertiesInternal(SessionId id, DotnetObjectId objectId, JToken args, CancellationToken token) { - if (objectId.Scheme == "scope") + var accessorPropertiesOnly = false; + var ownProperties = false; + if (args != null) { - return await GetScopeProperties(id, int.Parse(objectId.Value), token); + if (args["accessorPropertiesOnly"] != null) + accessorPropertiesOnly = args["accessorPropertiesOnly"].Value(); + if (args["ownProperties"] != null) + ownProperties = args["ownProperties"].Value(); } - - Result res = await SendMonoCommand(id, MonoCommands.GetDetails(objectId, args), token); - if (res.IsErr) - return res; - - if (objectId.Scheme == "cfo_res") - { - // Runtime.callFunctionOn result object - string value_json_str = res.Value["result"]?["value"]?["__value_as_json_string__"]?.Value(); - if (value_json_str != null) + //Console.WriteLine($"RuntimeGetProperties - {args}"); + try { + switch (objectId.Scheme) { - res = Result.OkFromObject(new + case "scope": { - result = JArray.Parse(value_json_str) - }); - } - else - { - res = Result.OkFromObject(new { result = new { } }); + var res = await GetScopeProperties(id, int.Parse(objectId.Value), token); + return res.Value?["result"]; + } + case "valuetype": + return await sdbHelper.GetValueTypeValues(id, int.Parse(objectId.Value), accessorPropertiesOnly, token); + case "array": + return await sdbHelper.GetArrayValues(id, int.Parse(objectId.Value), token); + case "object": + return await sdbHelper.GetObjectValues(id, int.Parse(objectId.Value), true, false, accessorPropertiesOnly, ownProperties, token); + case "pointer": + return new JArray{await sdbHelper.GetPointerContent(id, int.Parse(objectId.Value), token)}; + case "cfo_res": + { + Result res = await SendMonoCommand(id, MonoCommands.GetDetails(int.Parse(objectId.Value), args), token); + string value_json_str = res.Value["result"]?["value"]?["__value_as_json_string__"]?.Value(); + return value_json_str != null ? JArray.Parse(value_json_str) : null; + } + default: + return null; + } } - else - { - res = Result.Ok(JObject.FromObject(new { result = res.Value["result"]["value"] })); + catch (Exception) { + return null; } - - return res; } - private async Task EvaluateCondition(SessionId sessionId, ExecutionContext context, JObject mono_frame, Breakpoint bp, CancellationToken token) + private async Task EvaluateCondition(SessionId sessionId, ExecutionContext context, Frame mono_frame, Breakpoint bp, CancellationToken token) { if (string.IsNullOrEmpty(bp?.Condition) || mono_frame == null) return true; @@ -551,8 +617,7 @@ private async Task EvaluateCondition(SessionId sessionId, ExecutionContext if (bp.ConditionAlreadyEvaluatedWithError) return false; try { - var resolver = new MemberReferenceResolver(this, context, sessionId, mono_frame["frame_id"].Value(), logger); - + var resolver = new MemberReferenceResolver(this, context, sessionId, mono_frame.Id, logger); JObject retValue = await resolver.Resolve(condition, token); if (retValue == null) retValue = await EvaluateExpression.CompileAndRunTheExpression(condition, resolver, token); @@ -573,172 +638,124 @@ private async Task EvaluateCondition(SessionId sessionId, ExecutionContext } return false; } - - private async Task OnPause(SessionId sessionId, JObject args, CancellationToken token) + private async Task SendCallStack(SessionId sessionId, ExecutionContext context, string reason, int thread_id, Breakpoint bp, JObject data, IEnumerable orig_callframes, CancellationToken token) { - //FIXME we should send release objects every now and then? Or intercept those we inject and deal in the runtime - Result res = await SendMonoCommand(sessionId, MonoCommands.GetCallStack(), token); - IEnumerable orig_callframes = args?["callFrames"]?.Values(); - ExecutionContext context = GetContext(sessionId); - JObject data = null; - string reason = "other";//other means breakpoint - - if (res.IsErr) - { - //Give up and send the original call stack - return false; - } - - //step one, figure out where did we hit - JToken res_value = res.Value?["result"]?["value"]; - if (res_value == null || res_value is JValue) - { - //Give up and send the original call stack - return false; - } - - Log("verbose", $"call stack (err is {res.Error} value is:\n{res.Value}"); - int? bp_id = res_value?["breakpoint_id"]?.Value(); - Log("verbose", $"We just hit bp {bp_id}"); - if (!bp_id.HasValue) - { - //Give up and send the original call stack - return false; - } - - Breakpoint bp = context.BreakpointRequests.Values.SelectMany(v => v.Locations).FirstOrDefault(b => b.RemoteId == bp_id.Value); - var callFrames = new List(); - foreach (JObject frame in orig_callframes) - { - string function_name = frame["functionName"]?.Value(); - string url = frame["url"]?.Value(); - if ("mono_wasm_fire_bp" == function_name || "_mono_wasm_fire_bp" == function_name || - "_mono_wasm_fire_exception" == function_name) + var frames = new List(); + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(thread_id); + command_params_writer.Write(0); + command_params_writer.Write(-1); + var ret_debugger_cmd_reader = await sdbHelper.SendDebuggerAgentCommand(sessionId, CmdThread.GetFrameInfo, command_params, token); + var frame_count = ret_debugger_cmd_reader.ReadInt32(); + //Console.WriteLine("frame_count - " + frame_count); + for (int j = 0; j < frame_count; j++) { + var frame_id = ret_debugger_cmd_reader.ReadInt32(); + var method_id = ret_debugger_cmd_reader.ReadInt32(); + var il_pos = ret_debugger_cmd_reader.ReadInt32(); + var flags = ret_debugger_cmd_reader.ReadByte(); + var method_token = await sdbHelper.GetMethodToken(sessionId, method_id, token); + var assembly_id = await sdbHelper.GetAssemblyIdFromMethod(sessionId, method_id, token); + var assembly_name = await sdbHelper.GetAssemblyName(sessionId, assembly_id, token); + var method_name = await sdbHelper.GetMethodName(sessionId, method_id, token); + DebugStore store = await LoadStore(sessionId, token); + AssemblyInfo asm = store.GetAssemblyByName(assembly_name); + if (asm == null) { - if ("_mono_wasm_fire_exception" == function_name) + assembly_name = await sdbHelper.GetAssemblyNameFull(sessionId, assembly_id, token); //maybe is a lazy loaded assembly + asm = store.GetAssemblyByName(assembly_name); + if (asm == null) { - Result exception_obj_id = await SendMonoCommand(sessionId, MonoCommands.GetExceptionObject(), token); - JToken res_val = exception_obj_id.Value?["result"]?["value"]; - var exception_dotnet_obj_id = new DotnetObjectId("object", res_val?["exception_id"]?.Value()); - data = JObject.FromObject(new - { - type = "object", - subtype = "error", - className = res_val?["class_name"]?.Value(), - uncaught = res_val?["uncaught"]?.Value(), - description = res_val?["message"]?.Value() + "\n", - objectId = exception_dotnet_obj_id.ToString() - }); - reason = "exception"; + Log("debug", $"Unable to find assembly: {assembly_name}"); + continue; } + } - var frames = new List(); - IEnumerable the_mono_frames = res.Value?["result"]?["value"]?["frames"]?.Values(); + MethodInfo method = asm.GetMethodByToken(method_token); - foreach (JObject mono_frame in the_mono_frames) + if (method == null && !asm.HasSymbols) + { + try { - int frame_id = mono_frame["frame_id"].Value(); - int il_pos = mono_frame["il_pos"].Value(); - int method_token = mono_frame["method_token"].Value(); - string assembly_name = mono_frame["assembly_name"].Value(); + method = await LoadSymbolsOnDemand(asm, method_token, sessionId, token); + } + catch (Exception e) + { + Log("info", $"Unable to find il offset: {il_pos} in method token: {method_token} assembly name: {assembly_name} exception: {e}"); + continue; + } + } - // This can be different than `method.Name`, like in case of generic methods - string method_name = mono_frame["method_name"]?.Value(); + if (method == null) + { + Log("debug", $"Unable to find il offset: {il_pos} in method token: {method_token} assembly name: {assembly_name}"); + continue; + } - DebugStore store = await LoadStore(sessionId, token); - AssemblyInfo asm = store.GetAssemblyByName(assembly_name); - if (asm == null) - { - Log("debug", $"Unable to find assembly: {assembly_name}"); - continue; - } + method.DebuggerId = method_id; - MethodInfo method = asm.GetMethodByToken(method_token); + SourceLocation location = method?.GetLocationByIl(il_pos); - if (method == null && !asm.HasSymbols) - { - try - { - method = await LoadSymbolsOnDemand(asm, method_token, sessionId, token); - } - catch (Exception e) - { - Log("info", $"Unable to find il offset: {il_pos} in method token: {method_token} assembly name: {assembly_name} exception: {e}"); - continue; - } - } + // When hitting a breakpoint on the "IncrementCount" method in the standard + // Blazor project template, one of the stack frames is inside mscorlib.dll + // and we get location==null for it. It will trigger a NullReferenceException + // if we don't skip over that stack frame. + if (location == null) + { + continue; + } - if (method == null) - { - Log("debug", $"Unable to find il offset: {il_pos} in method token: {method_token} assembly name: {assembly_name}"); - continue; - } + Log("debug", $"frame il offset: {il_pos} method token: {method_token} assembly name: {assembly_name}"); + Log("debug", $"\tmethod {method_name} location: {location}"); + frames.Add(new Frame(method, location, frame_id)); - SourceLocation location = method?.GetLocationByIl(il_pos); + callFrames.Add(new + { + functionName = method_name, + callFrameId = $"dotnet:scope:{frame_id}", + functionLocation = method.StartLocation.AsLocation(), - // When hitting a breakpoint on the "IncrementCount" method in the standard - // Blazor project template, one of the stack frames is inside mscorlib.dll - // and we get location==null for it. It will trigger a NullReferenceException - // if we don't skip over that stack frame. - if (location == null) - { - continue; - } + location = location.AsLocation(), - Log("debug", $"frame il offset: {il_pos} method token: {method_token} assembly name: {assembly_name}"); - Log("debug", $"\tmethod {method_name} location: {location}"); - frames.Add(new Frame(method, location, frame_id)); + url = store.ToUrl(location), - callFrames.Add(new + scopeChain = new[] { - functionName = method_name, - callFrameId = $"dotnet:scope:{frame_id}", - functionLocation = method.StartLocation.AsLocation(), - - location = location.AsLocation(), - - url = store.ToUrl(location), - - scopeChain = new[] - { - new + new + { + type = "local", + @object = new { - type = "local", - @object = new - { - @type = "object", - className = "Object", - description = "Object", - objectId = $"dotnet:scope:{frame_id}", - }, - name = method_name, - startLocation = method.StartLocation.AsLocation(), - endLocation = method.EndLocation.AsLocation(), - } - } - }); - - context.CallStack = frames; + @type = "object", + className = "Object", + description = "Object", + objectId = $"dotnet:scope:{frame_id}", + }, + name = method_name, + startLocation = method.StartLocation.AsLocation(), + endLocation = method.EndLocation.AsLocation(), + } + } + }); - } - if (!await EvaluateCondition(sessionId, context, the_mono_frames?.First(), bp, token)) - { - await SendCommand(sessionId, "Debugger.resume", new JObject(), token); - return true; - } - } - else if (!(function_name.StartsWith("wasm-function", StringComparison.Ordinal) || - url.StartsWith("wasm://wasm/", StringComparison.Ordinal))) - { - callFrames.Add(frame); - } + context.CallStack = frames; + context.ThreadId = thread_id; } - string[] bp_list = new string[bp == null ? 0 : 1]; if (bp != null) bp_list[0] = bp.StackId; + foreach (JObject frame in orig_callframes) + { + string function_name = frame["functionName"]?.Value(); + string url = frame["url"]?.Value(); + if (!(function_name.StartsWith("wasm-function", StringComparison.Ordinal) || + url.StartsWith("wasm://wasm/", StringComparison.Ordinal) || function_name == "_mono_wasm_fire_debugger_agent_message")) + { + callFrames.Add(frame); + } + } var o = JObject.FromObject(new { callFrames, @@ -746,10 +763,72 @@ private async Task OnPause(SessionId sessionId, JObject args, Cancellation data, hitBreakpoints = bp_list, }); - + if (!await EvaluateCondition(sessionId, context, context.CallStack.First(), bp, token)) + { + await SendCommand(sessionId, "Debugger.resume", new JObject(), token); + return true; + } SendEvent(sessionId, "Debugger.paused", o, token); + return true; } + private async Task OnReceiveDebuggerAgentEvent(SessionId sessionId, JObject args, CancellationToken token) + { + Result res = await SendMonoCommand(sessionId, MonoCommands.GetDebuggerAgentBufferReceived(), token); + if (res.IsErr) + return false; + + ExecutionContext context = GetContext(sessionId); + byte[] newBytes = Convert.FromBase64String(res.Value?["result"]?["value"]?["value"]?.Value()); + var ret_debugger_cmd = new MemoryStream(newBytes); + var ret_debugger_cmd_reader = new MonoBinaryReader(ret_debugger_cmd); + ret_debugger_cmd_reader.ReadBytes(11); //skip HEADER_LEN + ret_debugger_cmd_reader.ReadByte(); //suspend_policy + var number_of_events = ret_debugger_cmd_reader.ReadInt32(); //number of events -> should be always one + for (int i = 0 ; i < number_of_events; i++) { + var event_kind = (EventKind)ret_debugger_cmd_reader.ReadByte(); //event kind + var request_id = ret_debugger_cmd_reader.ReadInt32(); //request id + if (event_kind == EventKind.Step) + await sdbHelper.ClearSingleStep(sessionId, request_id, token); + int thread_id = ret_debugger_cmd_reader.ReadInt32(); + switch (event_kind) + { + case EventKind.Exception: + { + string reason = "exception"; + int object_id = ret_debugger_cmd_reader.ReadInt32(); + var caught = ret_debugger_cmd_reader.ReadByte(); + var exceptionObject = await sdbHelper.GetObjectValues(sessionId, object_id, true, false, false, true, token); + var exceptionObjectMessage = exceptionObject.FirstOrDefault(attr => attr["name"].Value().Equals("message")); + var data = JObject.FromObject(new + { + type = "object", + subtype = "error", + className = await sdbHelper.GetClassNameFromObject(sessionId, object_id, token), + uncaught = caught == 0, + description = exceptionObjectMessage["value"]["value"].Value(), + objectId = $"dotnet:object:{object_id}" + }); + + var ret = await SendCallStack(sessionId, context, reason, thread_id, null, data, args?["callFrames"]?.Values(), token); + return ret; + } + case EventKind.UserBreak: + case EventKind.Step: + case EventKind.Breakpoint: + { + Breakpoint bp = context.BreakpointRequests.Values.SelectMany(v => v.Locations).FirstOrDefault(b => b.RemoteId == request_id); + string reason = "other";//other means breakpoint + int method_id = 0; + if (event_kind != EventKind.UserBreak) + method_id = ret_debugger_cmd_reader.ReadInt32(); + var ret = await SendCallStack(sessionId, context, reason, thread_id, bp, null, args?["callFrames"]?.Values(), token); + return ret; + } + } + } + return false; + } private async Task LoadSymbolsOnDemand(AssemblyInfo asm, int method_token, SessionId sessionId, CancellationToken token) { @@ -826,6 +905,7 @@ private async Task OnResume(MessageId msg_id, CancellationToken token) } //discard managed frames + sdbHelper.ClearCache(); GetContext(msg_id).ClearState(); } @@ -838,12 +918,9 @@ private async Task Step(MessageId msg_id, StepKind kind, CancellationToken if (context.CallStack.Count <= 1 && kind == StepKind.Out) return false; - Result res = await SendMonoCommand(msg_id, MonoCommands.StartSingleStepping(kind), token); - - int? ret_code = res.Value?["result"]?["value"]?.Value(); - - if (ret_code.HasValue && ret_code.Value == 0) - { + var step = await sdbHelper.Step(msg_id, context.ThreadId, kind, token); + if (step == false) { + sdbHelper.ClearCache(); context.ClearState(); await SendCommand(msg_id, "Debugger.stepOut", new JObject(), token); return false; @@ -971,24 +1048,21 @@ internal async Task GetScopeProperties(SessionId msg_id, int scope_id, C return Result.Err(JObject.FromObject(new { message = $"Could not find scope with id #{scope_id}" })); VarInfo[] var_ids = scope.Method.GetLiveVarsAt(scope.Location.CliLocation.Offset); - Result res = await SendMonoCommand(msg_id, MonoCommands.GetScopeVariables(scope.Id, var_ids), token); - - //if we fail we just buble that to the IDE (and let it panic over it) - if (res.IsErr) - return res; - JObject[] values = res.Value?["result"]?["value"]?.Values().ToArray(); - - if (values == null || values.Length == 0) - return Result.OkFromObject(new { result = Array.Empty() }); - - PerScopeCache frameCache = ctx.GetCacheForScope(scope_id); - foreach (JObject value in values) + var values = await sdbHelper.StackFrameGetValues(msg_id, scope.Method, ctx.ThreadId, scope_id, var_ids, token); + if (values != null) { - frameCache.Locals[value["name"]?.Value()] = value; - } + if (values == null || values.Count == 0) + return Result.OkFromObject(new { result = Array.Empty() }); - return Result.OkFromObject(new { result = values }); + PerScopeCache frameCache = ctx.GetCacheForScope(scope_id); + foreach (JObject value in values) + { + frameCache.Locals[value["name"]?.Value()] = value; + } + return Result.OkFromObject(new { result = values }); + } + return Result.OkFromObject(new { result = Array.Empty() }); } catch (Exception exception) { @@ -1004,16 +1078,16 @@ private async Task SetMonoBreakpoint(SessionId sessionId, string req int method_token = bp.Location.CliLocation.Method.Token; int il_offset = bp.Location.CliLocation.Offset; - Result res = await SendMonoCommand(sessionId, MonoCommands.SetBreakpoint(asm_name, method_token, il_offset), token); - int? ret_code = res.Value?["result"]?["value"]?.Value(); + var assembly_id = await sdbHelper.GetAssemblyId(sessionId, asm_name, token); + var method_id = await sdbHelper.GetMethodIdByToken(sessionId, assembly_id, method_token, token); + var breakpoint_id = await sdbHelper.SetBreakpoint(sessionId, method_id, il_offset, token); - if (ret_code.HasValue) + if (breakpoint_id > 0) { - bp.RemoteId = ret_code.Value; + bp.RemoteId = breakpoint_id; bp.State = BreakpointState.Active; //Log ("verbose", $"BP local id {bp.LocalId} enabled with remote id {bp.RemoteId}"); } - return bp; } @@ -1021,7 +1095,6 @@ private async Task OnSourceFileAdded(SessionId sessionId, SourceFile source, Exe { JObject scriptSource = JObject.FromObject(source.ToScriptSource(context.Id, context.AuxData)); Log("debug", $"sending {source.Url} {context.Id} {sessionId.sessionId}"); - SendEvent(sessionId, "Debugger.scriptParsed", scriptSource, token); foreach (var req in context.BreakpointRequests.Values) @@ -1033,7 +1106,7 @@ private async Task OnSourceFileAdded(SessionId sessionId, SourceFile source, Exe } } - private async Task LoadStore(SessionId sessionId, CancellationToken token) + internal async Task LoadStore(SessionId sessionId, CancellationToken token) { ExecutionContext context = GetContext(sessionId); @@ -1072,12 +1145,16 @@ private async Task RuntimeReady(SessionId sessionId, CancellationTok if (Interlocked.CompareExchange(ref context.ready, new TaskCompletionSource(), null) != null) return await context.ready.Task; - Result clear_result = await SendMonoCommand(sessionId, MonoCommands.ClearAllBreakpoints(), token); - if (clear_result.IsErr) + var command_params = new MemoryStream(); + var ret_debugger_cmd_reader = await sdbHelper.SendDebuggerAgentCommand(sessionId, CmdEventRequest.ClearAllBreakpoints, command_params, token); + if (ret_debugger_cmd_reader == null) { - Log("verbose", $"Failed to clear breakpoints due to {clear_result}"); + Log("verbose", $"Failed to clear breakpoints"); } + await sdbHelper.SetProtocolVersion(sessionId, token); + await sdbHelper.EnableReceiveUserBreakRequest(sessionId, token); + DebugStore store = await LoadStore(sessionId, token); context.ready.SetResult(store); @@ -1095,10 +1172,8 @@ private async Task RemoveBreakpoint(MessageId msg_id, JObject args, Cancellation foreach (Breakpoint bp in breakpointRequest.Locations) { - Result res = await SendMonoCommand(msg_id, MonoCommands.RemoveBreakpoint(bp.RemoteId), token); - int? ret_code = res.Value?["result"]?["value"]?.Value(); - - if (ret_code.HasValue) + var breakpoint_removed = await sdbHelper.RemoveBreakpoint(msg_id, bp.RemoteId, token); + if (breakpoint_removed) { bp.RemoteId = -1; bp.State = BreakpointState.Disabled; diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs new file mode 100644 index 0000000000000..c296c8501473a --- /dev/null +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -0,0 +1,1779 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System.Net.Http; +using System.Text.RegularExpressions; + +namespace Microsoft.WebAssembly.Diagnostics +{ + internal enum TokenType + { + MdtModule = 0x00000000, // + MdtTypeRef = 0x01000000, // + MdtTypeDef = 0x02000000, // + MdtFieldDef = 0x04000000, // + MdtMethodDef = 0x06000000, // + MdtParamDef = 0x08000000, // + MdtInterfaceImpl = 0x09000000, // + MdtMemberRef = 0x0a000000, // + MdtCustomAttribute = 0x0c000000, // + MdtPermission = 0x0e000000, // + MdtSignature = 0x11000000, // + MdtEvent = 0x14000000, // + MdtProperty = 0x17000000, // + MdtModuleRef = 0x1a000000, // + MdtTypeSpec = 0x1b000000, // + MdtAssembly = 0x20000000, // + MdtAssemblyRef = 0x23000000, // + MdtFile = 0x26000000, // + MdtExportedType = 0x27000000, // + MdtManifestResource = 0x28000000, // + MdtGenericParam = 0x2a000000, // + MdtMethodSpec = 0x2b000000, // + MdtGenericParamConstraint = 0x2c000000, + MdtString = 0x70000000, // + MdtName = 0x71000000, // + MdtBaseType = 0x72000000, // Leave this on the high end value. This does not correspond to metadata table + } + + internal enum CommandSet { + Vm = 1, + ObjectRef = 9, + StringRef = 10, + Thread = 11, + ArrayRef = 13, + EventRequest = 15, + StackFrame = 16, + AppDomain = 20, + Assembly = 21, + Method = 22, + Type = 23, + Module = 24, + Field = 25, + Event = 64, + Pointer = 65 + } + + internal enum EventKind { + VmStart = 0, + VmDeath = 1, + ThreadStart = 2, + ThreadDeath = 3, + AppDomainCreate = 4, + AppDomainUnload = 5, + MethodEntry = 6, + MethodExit = 7, + AssemblyLoad = 8, + AssemblyUnload = 9, + Breakpoint = 10, + Step = 11, + TypeLoad = 12, + Exception = 13, + KeepAlive = 14, + UserBreak = 15, + UserLog = 16, + Crash = 17 + } + + internal enum ModifierKind { + Count = 1, + ThreadOnly = 3, + LocationOnly = 7, + ExceptionOnly = 8, + Step = 10, + AssemblyOnly = 11, + SourceFileOnly = 12, + TypeNameOnly = 13 + } + + + internal enum SuspendPolicy { + None = 0, + EventThread = 1, + All = 2 + } + + internal enum CmdVM { + Version = 1, + AllThreads = 2, + Suspend = 3, + Resume = 4, + Exit = 5, + Dispose = 6, + InvokeMethod = 7, + SetProtocolVersion = 8, + AbortInvoke = 9, + SetKeepAlive = 10, + GetTypesForSourceFile = 11, + GetTypes = 12, + InvokeMethods = 13, + StartBuffering = 14, + StopBuffering = 15, + VmReadMemory = 16, + VmWriteMemory = 17, + GetAssemblyByName = 18 + } + + internal enum CmdFrame { + GetValues = 1, + GetThis = 2, + SetValues = 3, + GetDomain = 4, + SetThis = 5, + GetArgument = 6, + GetArguments = 7 + } + + internal enum CmdEvent { + Composite = 100 + } + + internal enum CmdThread { + GetFrameInfo = 1, + GetName = 2, + GetState = 3, + GetInfo = 4, + /* FIXME: Merge into GetInfo when the major protocol version is increased */ + GetId = 5, + /* Ditto */ + GetTid = 6, + SetIp = 7, + GetElapsedTime = 8 + } + + internal enum CmdEventRequest { + Set = 1, + Clear = 2, + ClearAllBreakpoints = 3 + } + + internal enum CmdAppDomain { + GetRootDomain = 1, + GetFriendlyName = 2, + GetAssemblies = 3, + GetEntryAssembly = 4, + CreateString = 5, + GetCorLib = 6, + CreateBoxedValue = 7, + CreateByteArray = 8, + } + + internal enum CmdAssembly { + GetLocation = 1, + GetEntryPoint = 2, + GetManifestModule = 3, + GetObject = 4, + GetType = 5, + GetName = 6, + GetDomain = 7, + GetMetadataBlob = 8, + GetIsDynamic = 9, + GetPdbBlob = 10, + GetTypeFromToken = 11, + GetMethodFromToken = 12, + HasDebugInfo = 13, + } + + internal enum CmdModule { + GetInfo = 1, + ApplyChanges = 2, + } + + internal enum CmdPointer{ + GetValue = 1 + } + + internal enum CmdMethod { + GetName = 1, + GetDeclaringType = 2, + GetDebugInfo = 3, + GetParamInfo = 4, + GetLocalsInfo = 5, + GetInfo = 6, + GetBody = 7, + ResolveToken = 8, + GetCattrs = 9, + MakeGenericMethod = 10, + Token = 11, + Assembly = 12, + ClassToken = 13, + AsyncDebugInfo = 14, + GetNameFull = 15 + } + + internal enum CmdType { + GetInfo = 1, + GetMethods = 2, + GetFields = 3, + GetValues = 4, + GetObject = 5, + GetSourceFiles = 6, + SetValues = 7, + IsAssignableFrom = 8, + GetProperties = 9, + GetCattrs = 10, + GetFieldCattrs = 11, + GetPropertyCattrs = 12, + /* FIXME: Merge into GetSourceFiles when the major protocol version is increased */ + GetSourceFiles2 = 13, + /* FIXME: Merge into GetValues when the major protocol version is increased */ + GetValues2 = 14, + GetMethodsByNameFlags = 15, + GetInterfaces = 16, + GetInterfacesMap = 17, + IsInitialized = 18, + CreateInstance = 19, + GetValueSize = 20, + GetValuesICorDbg = 21, + GetParents = 22 + } + + internal enum CmdArray { + GetLength = 1, + GetValues = 2, + SetValues = 3, + RefGetType = 4 + } + + + internal enum CmdField { + GetInfo = 1 + } + + internal enum CmdString { + GetValue = 1, + GetLength = 2, + GetChars = 3 + } + + internal enum CmdObject { + RefGetType = 1, + RefGetValues = 2, + RefIsCollected = 3, + RefGetAddress = 4, + RefGetDomain = 5, + RefSetValues = 6, + RefGetInfo = 7, + GetValuesICorDbg = 8, + RefDelegateGetMethod = 9, + RefIsDelegate = 10 + } + + internal enum ElementType { + End = 0x00, + Void = 0x01, + Boolean = 0x02, + Char = 0x03, + I1 = 0x04, + U1 = 0x05, + I2 = 0x06, + U2 = 0x07, + I4 = 0x08, + U4 = 0x09, + I8 = 0x0a, + U8 = 0x0b, + R4 = 0x0c, + R8 = 0x0d, + String = 0x0e, + Ptr = 0x0f, + ByRef = 0x10, + ValueType = 0x11, + Class = 0x12, + Var = 0x13, + Array = 0x14, + GenericInst = 0x15, + TypedByRef = 0x16, + I = 0x18, + U = 0x19, + FnPtr = 0x1b, + Object = 0x1c, + SzArray = 0x1d, + MVar = 0x1e, + CModReqD = 0x1f, + CModOpt = 0x20, + Internal = 0x21, + Modifier = 0x40, + Sentinel = 0x41, + Pinned = 0x45, + + Type = 0x50, + Boxed = 0x51, + Enum = 0x55 + } + + internal enum ValueTypeId { + Null = 0xf0, + Type = 0xf1, + VType = 0xf2, + FixedArray = 0xf3 + } + internal enum MonoTypeNameFormat{ + FormatIL, + FormatReflection, + FullName, + AssemblyQualified + } + + internal enum StepFilter { + None = 0, + StaticCtor = 1, + DebuggerHidden = 2, + DebuggerStepThrough = 4, + DebuggerNonUserCode = 8 + } + + internal class MonoBinaryReader : BinaryReader + { + public MonoBinaryReader(Stream stream) : base(stream) {} + + internal static unsafe void PutBytesBE (byte *dest, byte *src, int count) + { + int i = 0; + + if (BitConverter.IsLittleEndian){ + dest += count; + for (; i < count; i++) + *(--dest) = *src++; + } else { + for (; i < count; i++) + *dest++ = *src++; + } + } + + public override string ReadString() + { + var valueLen = ReadInt32(); + char[] value = new char[valueLen]; + Read(value, 0, valueLen); + return new string(value); + } + public unsafe long ReadLong() + { + byte[] data = new byte[8]; + Read(data, 0, 8); + + long ret; + fixed (byte *src = &data[0]){ + PutBytesBE ((byte *) &ret, src, 8); + } + + return ret; + } + public override unsafe sbyte ReadSByte() + { + byte[] data = new byte[4]; + Read(data, 0, 4); + + int ret; + fixed (byte *src = &data[0]){ + PutBytesBE ((byte *) &ret, src, 4); + } + return (sbyte)ret; + } + + public unsafe byte ReadUByte() + { + byte[] data = new byte[4]; + Read(data, 0, 4); + + int ret; + fixed (byte *src = &data[0]){ + PutBytesBE ((byte *) &ret, src, 4); + } + return (byte)ret; + } + + public override unsafe int ReadInt32() + { + byte[] data = new byte[4]; + Read(data, 0, 4); + int ret; + fixed (byte *src = &data[0]){ + PutBytesBE ((byte *) &ret, src, 4); + } + return ret; + } + + public override unsafe double ReadDouble() + { + byte[] data = new byte[8]; + Read(data, 0, 8); + + double ret; + fixed (byte *src = &data[0]){ + PutBytesBE ((byte *) &ret, src, 8); + } + return ret; + } + + public override unsafe uint ReadUInt32() + { + byte[] data = new byte[4]; + Read(data, 0, 4); + + uint ret; + fixed (byte *src = &data[0]){ + PutBytesBE ((byte *) &ret, src, 4); + } + return ret; + } + public unsafe ushort ReadUShort() + { + byte[] data = new byte[4]; + Read(data, 0, 4); + + uint ret; + fixed (byte *src = &data[0]){ + PutBytesBE ((byte *) &ret, src, 4); + } + return (ushort)ret; + } + } + + internal class MonoBinaryWriter : BinaryWriter + { + public MonoBinaryWriter(Stream stream) : base(stream) {} + public void WriteString(string val) + { + Write(val.Length); + Write(val.ToCharArray()); + } + public void WriteLong(long val) + { + Write((int)((val >> 32) & 0xffffffff)); + Write((int)((val >> 0) & 0xffffffff)); + } + public override void Write(int val) + { + byte[] bytes = BitConverter.GetBytes(val); + Array.Reverse(bytes, 0, bytes.Length); + Write(bytes); + } + public void WriteObj(DotnetObjectId objectId, MonoSDBHelper sdbHelper) + { + if (objectId.Scheme == "object") + { + Write((byte)ElementType.Class); + Write(int.Parse(objectId.Value)); + } + if (objectId.Scheme == "valuetype") + { + Write(sdbHelper.valueTypes[int.Parse(objectId.Value)].valueTypeBuffer); + } + } + } + internal class FieldTypeClass + { + public int Id { get; } + public string Name { get; } + public int TypeId { get; } + public FieldTypeClass(int id, string name, int typeId) + { + Id = id; + Name = name; + TypeId = typeId; + } + } + internal class ValueTypeClass + { + public byte[] valueTypeBuffer; + public JArray valueTypeJson; + public JArray valueTypeJsonProps; + public int typeId; + public JArray valueTypeProxy; + public string valueTypeVarName; + public bool valueTypeAutoExpand; + public int Id; + public ValueTypeClass(string varName, byte[] buffer, JArray json, int id, bool expand_properties, int valueTypeId) + { + valueTypeBuffer = buffer; + valueTypeJson = json; + typeId = id; + valueTypeJsonProps = null; + valueTypeProxy = null; + valueTypeVarName = varName; + valueTypeAutoExpand = expand_properties; + Id = valueTypeId; + } + } + internal class PointerValue + { + public long address; + public int typeId; + public string varName; + public PointerValue(long address, int typeId, string varName) + { + this.address = address; + this.typeId = typeId; + this.varName = varName; + } + + } + internal class MonoSDBHelper + { + internal Dictionary valueTypes = new Dictionary(); + internal Dictionary pointerValues = new Dictionary(); + private static int debugger_object_id; + private static int cmd_id; + private static int GetId() {return cmd_id++;} + private MonoProxy proxy; + private static int MINOR_VERSION = 61; + private static int MAJOR_VERSION = 2; + public MonoSDBHelper(MonoProxy proxy) + { + this.proxy = proxy; + } + + public void ClearCache() + { + valueTypes = new Dictionary(); + pointerValues = new Dictionary(); + } + + public async Task SetProtocolVersion(SessionId sessionId, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(MAJOR_VERSION); + command_params_writer.Write(MINOR_VERSION); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdVM.SetProtocolVersion, command_params, token); + return true; + } + public async Task EnableReceiveUserBreakRequest(SessionId sessionId, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write((byte)EventKind.UserBreak); + command_params_writer.Write((byte)SuspendPolicy.None); + command_params_writer.Write((byte)0); + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdEventRequest.Set, command_params, token); + return true; + } + internal async Task SendDebuggerAgentCommandInternal(SessionId sessionId, int command_set, int command, MemoryStream parms, CancellationToken token) + { + Result res = await proxy.SendMonoCommand(sessionId, MonoCommands.SendDebuggerAgentCommand(GetId(), command_set, command, Convert.ToBase64String(parms.ToArray())), token); + if (res.IsErr) { + throw new Exception($"SendDebuggerAgentCommand Error - {(CommandSet)command_set} - {command}"); + } + byte[] newBytes = Convert.FromBase64String(res.Value?["result"]?["value"]?["value"]?.Value()); + var ret_debugger_cmd = new MemoryStream(newBytes); + var ret_debugger_cmd_reader = new MonoBinaryReader(ret_debugger_cmd); + return ret_debugger_cmd_reader; + } + + internal CommandSet GetCommandSetForCommand(T command) => + command switch { + CmdVM => CommandSet.Vm, + CmdObject => CommandSet.ObjectRef, + CmdString => CommandSet.StringRef, + CmdThread => CommandSet.Thread, + CmdArray => CommandSet.ArrayRef, + CmdEventRequest => CommandSet.EventRequest, + CmdFrame => CommandSet.StackFrame, + CmdAppDomain => CommandSet.AppDomain, + CmdAssembly => CommandSet.Assembly, + CmdMethod => CommandSet.Method, + CmdType => CommandSet.Type, + CmdModule => CommandSet.Module, + CmdField => CommandSet.Field, + CmdEvent => CommandSet.Event, + CmdPointer => CommandSet.Pointer, + _ => throw new Exception ("Unknown CommandSet") + }; + + internal Task SendDebuggerAgentCommand(SessionId sessionId, T command, MemoryStream parms, CancellationToken token) where T : Enum => + SendDebuggerAgentCommandInternal(sessionId, (int)GetCommandSetForCommand(command), (int)(object)command, parms, token); + + internal Task SendDebuggerAgentCommandWithParms(SessionId sessionId, T command, MemoryStream parms, int type, string extraParm, CancellationToken token) where T : Enum => + SendDebuggerAgentCommandWithParmsInternal(sessionId, (int)GetCommandSetForCommand(command), (int)(object)command, parms, type, extraParm, token); + + internal async Task SendDebuggerAgentCommandWithParmsInternal(SessionId sessionId, int command_set, int command, MemoryStream parms, int type, string extraParm, CancellationToken token) + { + Result res = await proxy.SendMonoCommand(sessionId, MonoCommands.SendDebuggerAgentCommandWithParms(GetId(), command_set, command, Convert.ToBase64String(parms.ToArray()), parms.ToArray().Length, type, extraParm), token); + if (res.IsErr) { + throw new Exception("SendDebuggerAgentCommandWithParms Error"); + } + byte[] newBytes = Convert.FromBase64String(res.Value?["result"]?["value"]?["value"]?.Value()); + var ret_debugger_cmd = new MemoryStream(newBytes); + var ret_debugger_cmd_reader = new MonoBinaryReader(ret_debugger_cmd); + return ret_debugger_cmd_reader; + } + + public async Task GetMethodToken(SessionId sessionId, int method_id, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(method_id); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdMethod.Token, command_params, token); + return ret_debugger_cmd_reader.ReadInt32() & 0xffffff; //token + } + + public async Task GetMethodIdByToken(SessionId sessionId, int assembly_id, int method_token, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(assembly_id); + command_params_writer.Write(method_token | (int)TokenType.MdtMethodDef); + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdAssembly.GetMethodFromToken, command_params, token); + return ret_debugger_cmd_reader.ReadInt32(); + } + + public async Task GetAssemblyIdFromMethod(SessionId sessionId, int method_id, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(method_id); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdMethod.Assembly, command_params, token); + return ret_debugger_cmd_reader.ReadInt32(); //assembly_id + } + + public async Task GetAssemblyId(SessionId sessionId, string asm_name, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.WriteString(asm_name); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdVM.GetAssemblyByName, command_params, token); + return ret_debugger_cmd_reader.ReadInt32(); + } + + public async Task GetAssemblyName(SessionId sessionId, int assembly_id, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(assembly_id); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdAssembly.GetLocation, command_params, token); + return ret_debugger_cmd_reader.ReadString(); + } + + + public async Task GetAssemblyNameFull(SessionId sessionId, int assembly_id, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(assembly_id); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdAssembly.GetName, command_params, token); + var name = ret_debugger_cmd_reader.ReadString(); + return name.Remove(name.IndexOf(",")) + ".dll"; + } + + public async Task GetMethodName(SessionId sessionId, int method_id, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(method_id); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdMethod.GetNameFull, command_params, token); + var methodName = ret_debugger_cmd_reader.ReadString(); + return methodName.Substring(methodName.IndexOf(":")+1); + } + + public async Task MethodIsStatic(SessionId sessionId, int method_id, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(method_id); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdMethod.GetInfo, command_params, token); + var flags = ret_debugger_cmd_reader.ReadInt32(); + return (flags & 0x0010) > 0; //check method is static + } + + public async Task GetParamCount(SessionId sessionId, int method_id, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(method_id); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdMethod.GetParamInfo, command_params, token); + ret_debugger_cmd_reader.ReadInt32(); + int param_count = ret_debugger_cmd_reader.ReadInt32(); + return param_count; + } + + public async Task GetReturnType(SessionId sessionId, int method_id, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(method_id); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdMethod.GetParamInfo, command_params, token); + ret_debugger_cmd_reader.ReadInt32(); + ret_debugger_cmd_reader.ReadInt32(); + ret_debugger_cmd_reader.ReadInt32(); + var retType = ret_debugger_cmd_reader.ReadInt32(); + var ret = await GetTypeName(sessionId, retType, token); + return ret; + } + + public async Task GetParameters(SessionId sessionId, int method_id, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(method_id); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdMethod.GetParamInfo, command_params, token); + ret_debugger_cmd_reader.ReadInt32(); + var paramCount = ret_debugger_cmd_reader.ReadInt32(); + ret_debugger_cmd_reader.ReadInt32(); + var retType = ret_debugger_cmd_reader.ReadInt32(); + var parameters = "("; + for (int i = 0 ; i < paramCount; i++) + { + var paramType = ret_debugger_cmd_reader.ReadInt32(); + parameters += await GetTypeName(sessionId, paramType, token); + parameters = parameters.Replace("System.Func", "Func"); + if (i + 1 < paramCount) + parameters += ","; + } + parameters += ")"; + return parameters; + } + + public async Task SetBreakpoint(SessionId sessionId, int method_id, long il_offset, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write((byte)EventKind.Breakpoint); + command_params_writer.Write((byte)SuspendPolicy.None); + command_params_writer.Write((byte)1); + command_params_writer.Write((byte)ModifierKind.LocationOnly); + command_params_writer.Write(method_id); + command_params_writer.WriteLong(il_offset); + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdEventRequest.Set, command_params, token); + return ret_debugger_cmd_reader.ReadInt32(); + } + + public async Task RemoveBreakpoint(SessionId sessionId, int breakpoint_id, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write((byte)EventKind.Breakpoint); + command_params_writer.Write((int) breakpoint_id); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdEventRequest.Clear, command_params, token); + + if (ret_debugger_cmd_reader != null) + return true; + return false; + } + + public async Task Step(SessionId sessionId, int thread_id, StepKind kind, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write((byte)EventKind.Step); + command_params_writer.Write((byte)SuspendPolicy.None); + command_params_writer.Write((byte)1); + command_params_writer.Write((byte)ModifierKind.Step); + command_params_writer.Write(thread_id); + command_params_writer.Write((int)0); + command_params_writer.Write((int)kind); + command_params_writer.Write((int)(StepFilter.StaticCtor | StepFilter.DebuggerHidden)); //filter + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdEventRequest.Set, command_params, token); + if (ret_debugger_cmd_reader == null) + return false; + var isBPOnManagedCode = ret_debugger_cmd_reader.ReadInt32(); + if (isBPOnManagedCode == 0) + return false; + return true; + } + + public async Task ClearSingleStep(SessionId sessionId, int req_id, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write((byte)EventKind.Step); + command_params_writer.Write((int) req_id); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdEventRequest.Clear, command_params, token); + + if (ret_debugger_cmd_reader != null) + return true; + return false; + } + + public async Task> GetTypeFields(SessionId sessionId, int type_id, CancellationToken token) + { + var ret = new List(); + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(type_id); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdType.GetFields, command_params, token); + var nFields = ret_debugger_cmd_reader.ReadInt32(); + + for (int i = 0 ; i < nFields; i++) + { + int fieldId = ret_debugger_cmd_reader.ReadInt32(); //fieldId + string fieldNameStr = ret_debugger_cmd_reader.ReadString(); + int typeId = ret_debugger_cmd_reader.ReadInt32(); //typeId + ret_debugger_cmd_reader.ReadInt32(); //attrs + if (fieldNameStr.Contains("k__BackingField")) + { + fieldNameStr = fieldNameStr.Replace("k__BackingField", ""); + fieldNameStr = fieldNameStr.Replace("<", ""); + fieldNameStr = fieldNameStr.Replace(">", ""); + } + ret.Add(new FieldTypeClass(fieldId, fieldNameStr, typeId)); + } + return ret; + } + public string ReplaceCommonClassNames(string className) + { + className = className.Replace("System.String", "string"); + className = className.Replace("System.Boolean", "bool"); + className = className.Replace("System.Char", "char"); + className = className.Replace("System.Int32", "int"); + className = className.Replace("System.Object", "object"); + className = className.Replace("System.Void", "void"); + className = className.Replace("System.Byte", "byte"); + return className; + } + public async Task GetTypeName(SessionId sessionId, int type_id, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(type_id); + command_params_writer.Write((int) MonoTypeNameFormat.FormatReflection); + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdType.GetInfo, command_params, token); + + ret_debugger_cmd_reader.ReadString(); + + ret_debugger_cmd_reader.ReadString(); + + string className = ret_debugger_cmd_reader.ReadString(); + + className = className.Replace("+", "."); + className = Regex.Replace(className, @"`\d+", ""); + className = className.Replace("[]", "__SQUARED_BRACKETS__"); + className = className.Replace("[", "<"); + className = className.Replace("]", ">"); + className = className.Replace("__SQUARED_BRACKETS__", "[]"); + className = className.Replace(",", ", "); + className = ReplaceCommonClassNames(className); + return className; + } + + public async Task GetStringValue(SessionId sessionId, int string_id, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(string_id); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdString.GetValue, command_params, token); + var isUtf16 = ret_debugger_cmd_reader.ReadByte(); + if (isUtf16 == 0) { + return ret_debugger_cmd_reader.ReadString(); + } + return null; + } + public async Task GetArrayLength(SessionId sessionId, int object_id, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(object_id); + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdArray.GetLength, command_params, token); + var length = ret_debugger_cmd_reader.ReadInt32(); + length = ret_debugger_cmd_reader.ReadInt32(); + return length; + } + public async Task> GetTypeIdFromObject(SessionId sessionId, int object_id, bool withParents, CancellationToken token) + { + List ret = new List(); + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(object_id); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdObject.RefGetType, command_params, token); + var type_id = ret_debugger_cmd_reader.ReadInt32(); + ret.Add(type_id); + if (withParents) + { + command_params = new MemoryStream(); + command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(type_id); + ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdType.GetParents, command_params, token); + var parentsCount = ret_debugger_cmd_reader.ReadInt32(); + for (int i = 0 ; i < parentsCount; i++) + { + ret.Add(ret_debugger_cmd_reader.ReadInt32()); + } + } + return ret; + } + + public async Task GetClassNameFromObject(SessionId sessionId, int object_id, CancellationToken token) + { + var type_id = await GetTypeIdFromObject(sessionId, object_id, false, token); + return await GetTypeName(sessionId, type_id[0], token); + } + + public async Task GetMethodIdByName(SessionId sessionId, int type_id, string method_name, CancellationToken token) + { + var ret = new List(); + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write((int)type_id); + command_params_writer.WriteString(method_name); + command_params_writer.Write((int)(0x10 | 4)); //instance methods + command_params_writer.Write((int)1); //case sensitive + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdType.GetMethodsByNameFlags, command_params, token); + var nMethods = ret_debugger_cmd_reader.ReadInt32(); + return ret_debugger_cmd_reader.ReadInt32(); + } + + public async Task IsDelegate(SessionId sessionId, int objectId, CancellationToken token) + { + var ret = new List(); + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write((int)objectId); + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdObject.RefIsDelegate, command_params, token); + return ret_debugger_cmd_reader.ReadByte() == 1; + } + + public async Task GetDelegateMethod(SessionId sessionId, int objectId, CancellationToken token) + { + var ret = new List(); + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write((int)objectId); + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdObject.RefDelegateGetMethod, command_params, token); + return ret_debugger_cmd_reader.ReadInt32(); + } + + public async Task GetDelegateMethodDescription(SessionId sessionId, int objectId, CancellationToken token) + { + var methodId = await GetDelegateMethod(sessionId, objectId, token); + + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(methodId); + //Console.WriteLine("methodId - " + methodId); + if (methodId == 0) + return ""; + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdMethod.GetName, command_params, token); + var methodName = ret_debugger_cmd_reader.ReadString(); + + var returnType = await GetReturnType(sessionId, methodId, token); + var parameters = await GetParameters(sessionId, methodId, token); + + return $"{returnType} {methodName} {parameters}"; + } + public async Task InvokeMethod(SessionId sessionId, byte[] valueTypeBuffer, int method_id, string varName, CancellationToken token) + { + MemoryStream parms = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(parms); + command_params_writer.Write(method_id); + command_params_writer.Write(valueTypeBuffer); + command_params_writer.Write(0); + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdVM.InvokeMethod, parms, token); + ret_debugger_cmd_reader.ReadByte(); //number of objects returned. + return await CreateJObjectForVariableValue(sessionId, ret_debugger_cmd_reader, varName, false, -1, token); + } + public async Task CreateJArrayForProperties(SessionId sessionId, int typeId, byte[] object_buffer, JArray attributes, bool isAutoExpandable, string objectId, bool isOwn, CancellationToken token) + { + JArray ret = new JArray(); + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(typeId); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdType.GetProperties, command_params, token); + var nProperties = ret_debugger_cmd_reader.ReadInt32(); + for (int i = 0 ; i < nProperties; i++) + { + ret_debugger_cmd_reader.ReadInt32(); //propertyId + string propertyNameStr = ret_debugger_cmd_reader.ReadString(); + var getMethodId = ret_debugger_cmd_reader.ReadInt32(); + ret_debugger_cmd_reader.ReadInt32(); //setmethod + var attrs = ret_debugger_cmd_reader.ReadInt32(); //attrs + if (getMethodId == 0 || await GetParamCount(sessionId, getMethodId, token) != 0 || await MethodIsStatic(sessionId, getMethodId, token)) + continue; + JObject propRet = null; + if (attributes.Where(attribute => attribute["name"].Value().Equals(propertyNameStr)).Any()) + continue; + if (isAutoExpandable) + { + try { + propRet = await InvokeMethod(sessionId, object_buffer, getMethodId, propertyNameStr, token); + } + catch (Exception) + { + continue; + } + } + else + { + propRet = JObject.FromObject(new { + get = new + { + type = "function", + objectId = $"{objectId}:method_id:{getMethodId}", + className = "Function", + description = "get " + propertyNameStr + " ()", + methodId = getMethodId, + objectIdValue = objectId + }, + name = propertyNameStr + }); + } + if (isOwn) + propRet["isOwn"] = true; + ret.Add(propRet); + } + return ret; + } + public async Task GetPointerContent(SessionId sessionId, int pointerId, CancellationToken token) + { + var ret = new List(); + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.WriteLong(pointerValues[pointerId].address); + command_params_writer.Write(pointerValues[pointerId].typeId); + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdPointer.GetValue, command_params, token); + var varName = pointerValues[pointerId].varName; + if (int.TryParse(varName, out _)) + varName = $"[{varName}]"; + return await CreateJObjectForVariableValue(sessionId, ret_debugger_cmd_reader, "*" + varName, false, -1, token); + } + public async Task GetPropertiesValuesOfValueType(SessionId sessionId, int valueTypeId, CancellationToken token) + { + JArray ret = new JArray(); + var valueType = valueTypes[valueTypeId]; + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(valueType.typeId); + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdType.GetParents, command_params, token); + var parentsCount = ret_debugger_cmd_reader.ReadInt32(); + List typesToGetProperties = new List(); + typesToGetProperties.Add(valueType.typeId); + for (int i = 0 ; i < parentsCount; i++) + { + typesToGetProperties.Add(ret_debugger_cmd_reader.ReadInt32()); + } + for (int i = 0 ; i < typesToGetProperties.Count; i++) + { + var properties = await CreateJArrayForProperties(sessionId, typesToGetProperties[i], valueType.valueTypeBuffer, valueType.valueTypeJson, valueType.valueTypeAutoExpand, $"dotnet:valuetype:{valueType.Id}", i == 0, token); + ret = new JArray(ret.Union(properties)); + } + + return ret; + } + + public bool AutoExpandable(string className) { + if (className == "System.DateTime" || + className == "System.DateTimeOffset" || + className == "System.TimeSpan") + return true; + return false; + } + + public bool AutoInvokeToString(string className) { + if (className == "System.DateTime" || + className == "System.DateTimeOffset" || + className == "System.TimeSpan" || + className == "System.Decimal" || + className == "System.Guid") + return true; + return false; + } + + public JObject CreateJObject(T value, string type, string description, bool writable, string className = null, string objectId = null, string __custom_type = null, string subtype = null, bool isValueType = false, bool expanded = false, bool isEnum = false) + { + var ret = JObject.FromObject(new { + value = new + { + type, + value, + description + }, + writable + }); + if (__custom_type != null) + ret["value"]["__custom_type"] = __custom_type; + if (className != null) + ret["value"]["className"] = className; + if (objectId != null) + ret["value"]["objectId"] = objectId; + if (subtype != null) + ret["value"]["subtype"] = subtype; + if (isValueType) + ret["value"]["isValueType"] = isValueType; + if (expanded) + ret["value"]["expanded"] = expanded; + if (isEnum) + ret["value"]["isEnum"] = isEnum; + return ret; + + } + public JObject CreateJObjectForBoolean(int value) + { + return CreateJObject(value == 0 ? false : true, "boolean", value == 0 ? "false" : "true", true); + } + + public JObject CreateJObjectForNumber(T value) + { + return CreateJObject(value, "number", value.ToString(), true); + } + + public JObject CreateJObjectForChar(int value) + { + var description = $"{value.ToString()} '{Convert.ToChar(value)}'"; + return CreateJObject(description, "symbol", description, true); + } + + public async Task CreateJObjectForPtr(SessionId sessionId, ElementType etype, MonoBinaryReader ret_debugger_cmd_reader, string name, CancellationToken token) + { + string type; + string value; + long valueAddress = ret_debugger_cmd_reader.ReadLong(); + var typeId = ret_debugger_cmd_reader.ReadInt32(); + var className = ""; + if (etype == ElementType.FnPtr) + className = "(*())"; //to keep the old behavior + else + className = "(" + await GetTypeName(sessionId, typeId, token) + ")"; + + int pointerId = 0; + if (valueAddress != 0 && className != "(void*)") + { + pointerId = Interlocked.Increment(ref debugger_object_id); + type = "object"; + value = className; + pointerValues[pointerId] = new PointerValue(valueAddress, typeId, name); + } + else + { + type = "symbol"; + value = className + " " + valueAddress; + } + return CreateJObject(value, type, value, false, className, $"dotnet:pointer:{pointerId}", "pointer"); + } + + public async Task CreateJObjectForString(SessionId sessionId, MonoBinaryReader ret_debugger_cmd_reader, CancellationToken token) + { + var string_id = ret_debugger_cmd_reader.ReadInt32(); + var value = await GetStringValue(sessionId, string_id, token); + return CreateJObject(value, "string", value, false); + } + + public async Task CreateJObjectForArray(SessionId sessionId, MonoBinaryReader ret_debugger_cmd_reader, CancellationToken token) + { + var objectId = ret_debugger_cmd_reader.ReadInt32(); + var value = await GetClassNameFromObject(sessionId, objectId, token); + var length = await GetArrayLength(sessionId, objectId, token); + return CreateJObject(null, "object", $"{value.ToString()}({length})", false, value.ToString(), "dotnet:array:" + objectId, null, "array"); + } + + public async Task CreateJObjectForObject(SessionId sessionId, MonoBinaryReader ret_debugger_cmd_reader, int typeIdFromAttribute, CancellationToken token) + { + var objectId = ret_debugger_cmd_reader.ReadInt32(); + var className = ""; + var type_id = await GetTypeIdFromObject(sessionId, objectId, false, token); + className = await GetTypeName(sessionId, type_id[0], token); + var description = className.ToString(); + if (await IsDelegate(sessionId, objectId, token)) + { + if (typeIdFromAttribute != -1) + { + className = await GetTypeName(sessionId, typeIdFromAttribute, token); + } + + description = await GetDelegateMethodDescription(sessionId, objectId, token); + if (description == "") + { + return CreateJObject(className.ToString(), "symbol", className.ToString(), false); + } + } + return CreateJObject(null, "object", description, false, className, $"dotnet:object:{objectId}"); + } + + public async Task CreateJObjectForValueType(SessionId sessionId, MonoBinaryReader ret_debugger_cmd_reader, string name, long initialPos, CancellationToken token) + { + JObject fieldValueType = null; + var isEnum = ret_debugger_cmd_reader.ReadByte(); + var isBoxed = ret_debugger_cmd_reader.ReadByte() == 1; + var typeId = ret_debugger_cmd_reader.ReadInt32(); + var className = await GetTypeName(sessionId, typeId, token); + var description = className; + var numFields = ret_debugger_cmd_reader.ReadInt32(); + var fields = await GetTypeFields(sessionId, typeId, token); + JArray valueTypeFields = new JArray(); + if (className.IndexOf("System.Nullable<") == 0) //should we call something on debugger-agent to check??? + { + ret_debugger_cmd_reader.ReadByte(); //ignoring the boolean type + var isNull = ret_debugger_cmd_reader.ReadInt32(); + var value = await CreateJObjectForVariableValue(sessionId, ret_debugger_cmd_reader, name, false, -1, token); + if (isNull != 0) + return value; + else + return CreateJObject(null, "object", className, false, className, null, null, "null", true); + } + for (int i = 0; i < numFields ; i++) + { + fieldValueType = await CreateJObjectForVariableValue(sessionId, ret_debugger_cmd_reader, fields.ElementAt(i).Name, true, fields.ElementAt(i).TypeId, token); + valueTypeFields.Add(fieldValueType); + } + + long endPos = ret_debugger_cmd_reader.BaseStream.Position; + var valueTypeId = Interlocked.Increment(ref debugger_object_id); + + ret_debugger_cmd_reader.BaseStream.Position = initialPos; + byte[] valueTypeBuffer = new byte[endPos - initialPos]; + ret_debugger_cmd_reader.Read(valueTypeBuffer, 0, (int)(endPos - initialPos)); + ret_debugger_cmd_reader.BaseStream.Position = endPos; + valueTypes[valueTypeId] = new ValueTypeClass(name, valueTypeBuffer, valueTypeFields, typeId, AutoExpandable(className), valueTypeId); + if (AutoInvokeToString(className) || isEnum == 1) { + int method_id = await GetMethodIdByName(sessionId, typeId, "ToString", token); + var retMethod = await InvokeMethod(sessionId, valueTypeBuffer, method_id, "methodRet", token); + description = retMethod["value"]?["value"].Value(); + if (className.Equals("System.Guid")) + description = description.ToUpper(); //to keep the old behavior + } + else if (isBoxed && numFields == 1) { + return fieldValueType; + } + return CreateJObject(null, "object", description, false, className, $"dotnet:valuetype:{valueTypeId}", null, null, true, true, isEnum == 1); + } + + public async Task CreateJObjectForNull(SessionId sessionId, MonoBinaryReader ret_debugger_cmd_reader, CancellationToken token) + { + string className = ""; + ElementType variableType = (ElementType)ret_debugger_cmd_reader.ReadByte(); + switch (variableType) + { + case ElementType.String: + case ElementType.Class: + { + var type_id = ret_debugger_cmd_reader.ReadInt32(); + className = await GetTypeName(sessionId, type_id, token); + break; + + } + case ElementType.SzArray: + case ElementType.Array: + { + ElementType byte_type = (ElementType)ret_debugger_cmd_reader.ReadByte(); + var rank = ret_debugger_cmd_reader.ReadInt32(); + if (byte_type == ElementType.Class) { + var internal_type_id = ret_debugger_cmd_reader.ReadInt32(); + } + var type_id = ret_debugger_cmd_reader.ReadInt32(); + className = await GetTypeName(sessionId, type_id, token); + break; + } + default: + { + var type_id = ret_debugger_cmd_reader.ReadInt32(); + className = await GetTypeName(sessionId, type_id, token); + break; + } + } + return CreateJObject(null, "object", className, false, className, null, null, "null"); + } + + public async Task CreateJObjectForVariableValue(SessionId sessionId, MonoBinaryReader ret_debugger_cmd_reader, string name, bool isOwn, int typeIdFromAttribute, CancellationToken token) + { + long initialPos = ret_debugger_cmd_reader == null ? 0 : ret_debugger_cmd_reader.BaseStream.Position; + ElementType etype = (ElementType)ret_debugger_cmd_reader.ReadByte(); + JObject ret = null; + switch (etype) { + case ElementType.I: + case ElementType.U: + case ElementType.Void: + case (ElementType)ValueTypeId.Type: + case (ElementType)ValueTypeId.VType: + case (ElementType)ValueTypeId.FixedArray: + ret = new JObject{{"Type", "void"}}; + break; + case ElementType.Boolean: + { + var value = ret_debugger_cmd_reader.ReadInt32(); + ret = CreateJObjectForBoolean(value); + break; + } + case ElementType.I1: + { + var value = ret_debugger_cmd_reader.ReadSByte(); + ret = CreateJObjectForNumber(value); + break; + } + case ElementType.I2: + case ElementType.I4: + { + var value = ret_debugger_cmd_reader.ReadInt32(); + ret = CreateJObjectForNumber(value); + break; + } + case ElementType.U1: + { + var value = ret_debugger_cmd_reader.ReadUByte(); + ret = CreateJObjectForNumber(value); + break; + } + case ElementType.U2: + { + var value = ret_debugger_cmd_reader.ReadUShort(); + ret = CreateJObjectForNumber(value); + break; + } + case ElementType.U4: + { + var value = ret_debugger_cmd_reader.ReadUInt32(); + ret = CreateJObjectForNumber(value); + break; + } + case ElementType.R4: + { + float value = BitConverter.Int32BitsToSingle(ret_debugger_cmd_reader.ReadInt32()); + ret = CreateJObjectForNumber(value); + break; + } + case ElementType.Char: + { + var value = ret_debugger_cmd_reader.ReadInt32(); + ret = CreateJObjectForChar(value); + break; + } + case ElementType.I8: + { + long value = ret_debugger_cmd_reader.ReadLong(); + ret = CreateJObjectForNumber(value); + break; + } + case ElementType.U8: + { + ulong high = (ulong) ret_debugger_cmd_reader.ReadInt32(); + ulong low = (ulong) ret_debugger_cmd_reader.ReadInt32(); + var value = ((high << 32) | low); + ret = CreateJObjectForNumber(value); + break; + } + case ElementType.R8: + { + double value = ret_debugger_cmd_reader.ReadDouble(); + ret = CreateJObjectForNumber(value); + break; + } + case ElementType.FnPtr: + case ElementType.Ptr: + { + ret = await CreateJObjectForPtr(sessionId, etype, ret_debugger_cmd_reader, name, token); + break; + } + case ElementType.String: + { + ret = await CreateJObjectForString(sessionId, ret_debugger_cmd_reader, token); + break; + } + case ElementType.SzArray: + case ElementType.Array: + { + ret = await CreateJObjectForArray(sessionId, ret_debugger_cmd_reader, token); + break; + } + case ElementType.Class: + case ElementType.Object: + { + ret = await CreateJObjectForObject(sessionId, ret_debugger_cmd_reader, typeIdFromAttribute, token); + break; + } + case ElementType.ValueType: + { + ret = await CreateJObjectForValueType(sessionId, ret_debugger_cmd_reader, name, initialPos, token); + break; + } + case (ElementType)ValueTypeId.Null: + { + ret = await CreateJObjectForNull(sessionId, ret_debugger_cmd_reader, token); + break; + } + } + if (isOwn) + ret["isOwn"] = true; + ret["name"] = name; + return ret; + } + + public async Task IsAsyncMethod(SessionId sessionId, int methodId, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(methodId); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdMethod.AsyncDebugInfo, command_params, token); + return ret_debugger_cmd_reader.ReadByte() == 1 ; //token + } + + public async Task StackFrameGetValues(SessionId sessionId, MethodInfo method, int thread_id, int frame_id, VarInfo[] var_ids, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + MonoBinaryReader ret_debugger_cmd_reader = null; + command_params_writer.Write(thread_id); + command_params_writer.Write(frame_id); + command_params_writer.Write(var_ids.Length); + foreach (var var in var_ids) + { + command_params_writer.Write(var.Index); + } + + if (await IsAsyncMethod(sessionId, method.DebuggerId, token)) + { + ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdFrame.GetThis, command_params, token); + ret_debugger_cmd_reader.ReadByte(); //ignore type + var objectId = ret_debugger_cmd_reader.ReadInt32(); + var asyncLocals = await GetObjectValues(sessionId, objectId, true, false, false, false, token); + asyncLocals = new JArray(asyncLocals.Where( asyncLocal => !asyncLocal["name"].Value().Contains("<>") || asyncLocal["name"].Value().EndsWith("__this"))); + foreach (var asyncLocal in asyncLocals) + { + if (asyncLocal["name"].Value().EndsWith("__this")) + asyncLocal["name"] = "this"; + else if (asyncLocal["name"].Value().Contains("<")) + asyncLocal["name"] = Regex.Match(asyncLocal["name"].Value(), @"\<([^)]*)\>").Groups[1].Value; + } + return asyncLocals; + } + + JArray locals = new JArray(); + ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdFrame.GetValues, command_params, token); + foreach (var var in var_ids) + { + var var_json = await CreateJObjectForVariableValue(sessionId, ret_debugger_cmd_reader, var.Name, false, -1, token); + locals.Add(var_json); + } + if (!method.IsStatic()) + { + ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdFrame.GetThis, command_params, token); + var var_json = await CreateJObjectForVariableValue(sessionId, ret_debugger_cmd_reader, "this", false, -1, token); + var_json.Add("fieldOffset", -1); + locals.Add(var_json); + } + return locals; + + } + + public async Task GetValueTypeValues(SessionId sessionId, int valueTypeId, bool accessorPropertiesOnly, CancellationToken token) + { + if (valueTypes[valueTypeId].valueTypeJsonProps == null) + { + valueTypes[valueTypeId].valueTypeJsonProps = await GetPropertiesValuesOfValueType(sessionId, valueTypeId, token); + } + if (accessorPropertiesOnly) + return valueTypes[valueTypeId].valueTypeJsonProps; + var ret = new JArray(valueTypes[valueTypeId].valueTypeJson.Union(valueTypes[valueTypeId].valueTypeJsonProps)); + return ret; + } + + public async Task GetValueTypeProxy(SessionId sessionId, int valueTypeId, CancellationToken token) + { + if (valueTypes[valueTypeId].valueTypeProxy != null) + return valueTypes[valueTypeId].valueTypeProxy; + valueTypes[valueTypeId].valueTypeProxy = new JArray(valueTypes[valueTypeId].valueTypeJson); + + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(valueTypes[valueTypeId].typeId); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdType.GetProperties, command_params, token); + var nProperties = ret_debugger_cmd_reader.ReadInt32(); + + for (int i = 0 ; i < nProperties; i++) + { + ret_debugger_cmd_reader.ReadInt32(); //propertyId + string propertyNameStr = ret_debugger_cmd_reader.ReadString(); + + var getMethodId = ret_debugger_cmd_reader.ReadInt32(); + ret_debugger_cmd_reader.ReadInt32(); //setmethod + ret_debugger_cmd_reader.ReadInt32(); //attrs + if (await MethodIsStatic(sessionId, getMethodId, token)) + continue; + var command_params_to_proxy = new MemoryStream(); + var command_params_writer_to_proxy = new MonoBinaryWriter(command_params_to_proxy); + command_params_writer_to_proxy.Write(getMethodId); + command_params_writer_to_proxy.Write(valueTypes[valueTypeId].valueTypeBuffer); + command_params_writer_to_proxy.Write(0); + valueTypes[valueTypeId].valueTypeProxy.Add(JObject.FromObject(new { + get = JObject.FromObject(new { + commandSet = CommandSet.Vm, + command = CmdVM.InvokeMethod, + buffer = Convert.ToBase64String(command_params_to_proxy.ToArray()), + length = command_params_to_proxy.ToArray().Length + }), + name = propertyNameStr + })); + } + return valueTypes[valueTypeId].valueTypeProxy; + } + + public async Task GetArrayValues(SessionId sessionId, int arrayId, CancellationToken token) + { + var length = await GetArrayLength(sessionId, arrayId, token); + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(arrayId); + command_params_writer.Write(0); + command_params_writer.Write(length); + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdArray.GetValues, command_params, token); + JArray array = new JArray(); + for (int i = 0 ; i < length ; i++) + { + var var_json = await CreateJObjectForVariableValue(sessionId, ret_debugger_cmd_reader, i.ToString(), false, -1, token); + array.Add(var_json); + } + return array; + } + public async Task EnableExceptions(SessionId sessionId, string state, CancellationToken token) + { + + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write((byte)EventKind.Exception); + command_params_writer.Write((byte)SuspendPolicy.None); + command_params_writer.Write((byte)1); + command_params_writer.Write((byte)ModifierKind.ExceptionOnly); + command_params_writer.Write(0); //exc_class + if (state == "all") + command_params_writer.Write((byte)1); //caught + else + command_params_writer.Write((byte)0); //caught + if (state == "uncaught" || state == "all") + command_params_writer.Write((byte)1); //uncaught + else + command_params_writer.Write((byte)0); //uncaught + command_params_writer.Write((byte)1);//subclasses + command_params_writer.Write((byte)0);//not_filtered_feature + command_params_writer.Write((byte)0);//everything_else + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdEventRequest.Set, command_params, token); + return true; + } + public async Task GetObjectValues(SessionId sessionId, int objectId, bool withProperties, bool withSetter, bool accessorPropertiesOnly, bool ownProperties, CancellationToken token) + { + var typeId = await GetTypeIdFromObject(sessionId, objectId, true, token); + var className = await GetTypeName(sessionId, typeId[0], token); + JArray ret = new JArray(); + if (await IsDelegate(sessionId, objectId, token)) + { + var description = await GetDelegateMethodDescription(sessionId, objectId, token); + + var obj = JObject.FromObject(new { + value = new + { + type = "symbol", + value = description, + description + }, + name = "Target" + }); + ret.Add(obj); + return ret; + } + for (int i = 0; i < typeId.Count; i++) + { + if (!accessorPropertiesOnly) + { + var fields = await GetTypeFields(sessionId, typeId[i], token); + JArray objectFields = new JArray(); + + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(objectId); + command_params_writer.Write(fields.Count); + foreach (var field in fields) + { + command_params_writer.Write(field.Id); + } + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdObject.RefGetValues, command_params, token); + + foreach (var field in fields) + { + long initialPos = ret_debugger_cmd_reader.BaseStream.Position; + int valtype = ret_debugger_cmd_reader.ReadByte(); + ret_debugger_cmd_reader.BaseStream.Position = initialPos; + var fieldValue = await CreateJObjectForVariableValue(sessionId, ret_debugger_cmd_reader, field.Name, i == 0, field.TypeId, token); + + if (ret.Where(attribute => attribute["name"].Value().Equals(fieldValue["name"].Value())).Any()) { + continue; + } + if (withSetter) + { + var command_params_to_set = new MemoryStream(); + var command_params_writer_to_set = new MonoBinaryWriter(command_params_to_set); + command_params_writer_to_set.Write(objectId); + command_params_writer_to_set.Write(1); + command_params_writer_to_set.Write(field.Id); + + fieldValue.Add("set", JObject.FromObject(new { + commandSet = CommandSet.ObjectRef, + command = CmdObject.RefSetValues, + buffer = Convert.ToBase64String(command_params_to_set.ToArray()), + valtype, + length = command_params_to_set.ToArray().Length + })); + } + objectFields.Add(fieldValue); + } + ret = new JArray(ret.Union(objectFields)); + } + if (!withProperties) + return ret; + var command_params_obj = new MemoryStream(); + var command_params_obj_writer = new MonoBinaryWriter(command_params_obj); + command_params_obj_writer.WriteObj(new DotnetObjectId("object", $"{objectId}"), this); + var props = await CreateJArrayForProperties(sessionId, typeId[i], command_params_obj.ToArray(), ret, false, $"dotnet:object:{objectId}", i == 0, token); + ret = new JArray(ret.Union(props)); + + // ownProperties + // Note: ownProperties should mean that we return members of the klass itself, + // but we are going to ignore that here, because otherwise vscode/chrome don't + // seem to ask for inherited fields at all. + //if (ownProperties) + //break; + /*if (accessorPropertiesOnly) + break;*/ + } + if (accessorPropertiesOnly) + { + var retAfterRemove = new JArray(); + List> allFields = new List>(); + for (int i = 0; i < typeId.Count; i++) + { + var fields = await GetTypeFields(sessionId, typeId[i], token); + allFields.Add(fields); + } + foreach (var item in ret) + { + bool foundField = false; + for (int j = 0 ; j < allFields.Count; j++) + { + foreach (var field in allFields[j]) + { + if (field.Name.Equals(item["name"].Value())) { + if (item["isOwn"] == null || (item["isOwn"].Value() && j == 0) || !item["isOwn"].Value()) + foundField = true; + break; + } + } + if (foundField) + break; + } + if (!foundField) { + retAfterRemove.Add(item); + } + } + ret = retAfterRemove; + } + return ret; + } + + public async Task GetObjectProxy(SessionId sessionId, int objectId, CancellationToken token) + { + var ret = await GetObjectValues(sessionId, objectId, false, true, false, false, token); + var typeIds = await GetTypeIdFromObject(sessionId, objectId, true, token); + foreach (var typeId in typeIds) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(typeId); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdType.GetProperties, command_params, token); + var nProperties = ret_debugger_cmd_reader.ReadInt32(); + for (int i = 0 ; i < nProperties; i++) + { + ret_debugger_cmd_reader.ReadInt32(); //propertyId + string propertyNameStr = ret_debugger_cmd_reader.ReadString(); + var getMethodId = ret_debugger_cmd_reader.ReadInt32(); + var setMethodId = ret_debugger_cmd_reader.ReadInt32(); //setmethod + var attrValue = ret_debugger_cmd_reader.ReadInt32(); //attrs + //Console.WriteLine($"{propertyNameStr} - {attrValue}"); + if (ret.Where(attribute => attribute["name"].Value().Equals(propertyNameStr)).Any()) + { + var attr = ret.Where(attribute => attribute["name"].Value().Equals(propertyNameStr)).First(); + + var command_params_to_set = new MemoryStream(); + var command_params_writer_to_set = new MonoBinaryWriter(command_params_to_set); + command_params_writer_to_set.Write(setMethodId); + command_params_writer_to_set.Write((byte)ElementType.Class); + command_params_writer_to_set.Write(objectId); + command_params_writer_to_set.Write(1); + if (attr["set"] != null) + { + attr["set"] = JObject.FromObject(new { + commandSet = CommandSet.Vm, + command = CmdVM.InvokeMethod, + buffer = Convert.ToBase64String(command_params_to_set.ToArray()), + valtype = attr["set"]["valtype"], + length = command_params_to_set.ToArray().Length + }); + } + continue; + } + else + { + var command_params_to_get = new MemoryStream(); + var command_params_writer_to_get = new MonoBinaryWriter(command_params_to_get); + command_params_writer_to_get.Write(getMethodId); + command_params_writer_to_get.Write((byte)ElementType.Class); + command_params_writer_to_get.Write(objectId); + command_params_writer_to_get.Write(0); + + ret.Add(JObject.FromObject(new { + get = JObject.FromObject(new { + commandSet = CommandSet.Vm, + command = CmdVM.InvokeMethod, + buffer = Convert.ToBase64String(command_params_to_get.ToArray()), + length = command_params_to_get.ToArray().Length + }), + name = propertyNameStr + })); + } + if (await MethodIsStatic(sessionId, getMethodId, token)) + continue; + } + } + return ret; + } + + public async Task SetVariableValue(SessionId sessionId, int thread_id, int frame_id, int varId, string newValue, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + MonoBinaryReader ret_debugger_cmd_reader = null; + command_params_writer.Write(thread_id); + command_params_writer.Write(frame_id); + command_params_writer.Write(1); + command_params_writer.Write(varId); + JArray locals = new JArray(); + ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdFrame.GetValues, command_params, token); + int etype = ret_debugger_cmd_reader.ReadByte(); + try + { + ret_debugger_cmd_reader = await SendDebuggerAgentCommandWithParms(sessionId, CmdFrame.SetValues, command_params, etype, newValue, token); + } + catch (Exception) + { + return false; + } + + return true; + } + } +} diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/AssignmentTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/AssignmentTests.cs index 00f917c8dc573..1afa97939d5ef 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/AssignmentTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/AssignmentTests.cs @@ -33,7 +33,7 @@ public class AssignmentTests : DebuggerTestBase { "MONO_TYPE_U2", TNumber(0), TNumber(1) }, { "MONO_TYPE_U4", TNumber(0), TNumber(1) }, { "MONO_TYPE_U8", TNumber(0), TNumber(1) }, - { "MONO_TYPE_R4", TNumber(0), TNumber("3.1414999961853027") }, + { "MONO_TYPE_R4", TNumber(0), TNumber("3.1415") }, //this is also the value that we see if we debug using VS { "MONO_TYPE_R8", TNumber(0), TNumber("3.1415") }, }; diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs index 9b41983812074..3d9106959c812 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs @@ -706,7 +706,6 @@ internal async Task CheckValue(JToken actual_val, JToken exp_val, string label) var exp_val_str = jp.Value.Value(); bool null_or_empty_exp_val = String.IsNullOrEmpty(exp_val_str); - var actual_field_val = actual_val?.Values()?.FirstOrDefault(a_jp => a_jp.Name == jp.Name); var actual_field_val_str = actual_field_val?.Value?.Value(); if (null_or_empty_exp_val && String.IsNullOrEmpty(actual_field_val_str)) diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/MonoJsTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/MonoJsTests.cs index e2b796062e308..4054c6a2cea11 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/MonoJsTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/MonoJsTests.cs @@ -13,85 +13,6 @@ namespace DebuggerTests { public class MonoJsTests : DebuggerTestBase { - [Fact] - public async Task FixupNameValueObjectsWithMissingParts() - { - var bp1_res = await SetBreakpointInMethod("debugger-test.dll", "Math", "IntAdd", 3); - - var names = new JObject[] - { - JObject.FromObject(new { name = "Abc" }), - JObject.FromObject(new { name = "Def" }), - JObject.FromObject(new { name = "Xyz" }) - }; - - var values = new JObject[] - { - JObject.FromObject(new { value = TObject("testclass") }), - JObject.FromObject(new { value = TString("test string") }), - }; - - var getters = new JObject[] - { - GetterRes("xyz"), - GetterRes("unattached") - }; - - var list = new[] { names[0], names[1], values[0], names[2], getters[0], getters[1] }; - var res = await cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression = $"MONO._fixup_name_value_objects({JsonConvert.SerializeObject(list)})", returnByValue = true }), token); - Assert.True(res.IsOk); - - await CheckProps(res.Value["result"]["value"], new - { - Abc = TSymbol(""), - Def = TObject("testclass"), - Xyz = TGetter("xyz") - }, "#1", num_fields: 4); - - JObject.DeepEquals(getters[1], res.Value["result"]["value"].Values().ToArray()[3]); - - static JObject GetterRes(string name) => JObject.FromObject(new - { - get = new - { - className = "Function", - description = $"get {name} () {{}}", - type = "function" - } - }); - } - - [Fact] - public async Task GetParamsAndLocalsWithInvalidIndices() - { - var bp1_res = await SetBreakpointInMethod("debugger-test.dll", "Math", "IntAdd", 3); - var pause_location = await EvaluateAndCheck( - "window.setTimeout(function() { invoke_static_method('[debugger-test] Math:IntAdd', 1, 2); })", - null, -1, -1, "IntAdd"); - - var scope_id = pause_location["callFrames"][0]["callFrameId"].Value(); - var scope = int.Parse(scope_id.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries)[2]); - - var var_ids = new[] - { - new { index = 0, name = "one" }, - new { index = -12, name = "bad0" }, - new { index = 1231, name = "bad1" } - }; - - var expression = $"MONO.mono_wasm_get_variables({scope}, {JsonConvert.SerializeObject(var_ids)})"; - - var res = await cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression, returnByValue = true }), token); - Assert.True(res.IsOk); - - await CheckProps(res.Value["result"]?["value"], new - { - one = TNumber(3), - bad0 = TSymbol(""), - bad1 = TSymbol("") - }, "results"); - } - [Fact] public async Task InvalidScopeId() { diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs index a671ed2b7c181..468ca4d628d37 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs @@ -641,43 +641,6 @@ await CompareObjectPropertiesFor(frame_locals, "this", }); - [Fact] - public async Task InvalidValueTypeData() - { - await CheckInspectLocalsAtBreakpointSite( - "dotnet://debugger-test.dll/debugger-test.cs", 85, 8, - "OuterMethod", - "window.setTimeout(function() { invoke_static_method ('[debugger-test] Math:OuterMethod'); })", - wait_for_event_fn: async (pause_location) => - { - var new_id = await CreateNewId(@"MONO._new_or_add_id_props ({ scheme: 'valuetype', idArgs: { containerId: 1 }, props: { klass: 3, value64: 4 }});"); - await _invoke_getter(new_id, "NonExistant", expect_ok: false); - - new_id = await CreateNewId(@"MONO._new_or_add_id_props ({ scheme: 'valuetype', idArgs: { containerId: 1 }, props: { klass: 3 }});"); - await _invoke_getter(new_id, "NonExistant", expect_ok: false); - - new_id = await CreateNewId(@"MONO._new_or_add_id_props ({ scheme: 'valuetype', idArgs: { containerId: 1 }, props: { klass: 3, value64: 'AA' }});"); - await _invoke_getter(new_id, "NonExistant", expect_ok: false); - }); - - async Task CreateNewId(string expr) - { - var res = await cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = expr }), token); - Assert.True(res.IsOk, "Expected Runtime.evaluate to succeed"); - AssertEqual("string", res.Value["result"]?["type"]?.Value(), "Expected Runtime.evaluate to return a string type result"); - return res.Value["result"]?["value"]?.Value(); - } - - async Task _invoke_getter(string obj_id, string property_name, bool expect_ok) - { - var expr = $"MONO._invoke_getter ('{obj_id}', '{property_name}')"; - var res = await cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = expr }), token); - AssertEqual(expect_ok, res.IsOk, "Runtime.evaluate result not as expected for {expr}"); - - return res; - } - } - [Fact] public async Task MulticastDelegateTest() => await CheckInspectLocalsAtBreakpointSite( "MulticastDelegateTestClass", "Test", 5, "Test", @@ -746,7 +709,7 @@ public async Task PreviousFrameForAReflectedCall() => await CheckInspectLocalsAt await CheckProps(frame_locals, new { - mi = TObject("System.Reflection.MethodInfo"), + mi = TObject("System.Reflection.RuntimeMethodInfo"), //this is what is returned when debugging desktop apps using VS dt = TDateTime(new DateTime(4210, 3, 4, 5, 6, 7)), i = TNumber(4), strings = TArray("string[]", 1), diff --git a/src/mono/wasm/runtime/library_mono.js b/src/mono/wasm/runtime/library_mono.js index e9cb8cd2b401d..84fb5684abd80 100644 --- a/src/mono/wasm/runtime/library_mono.js +++ b/src/mono/wasm/runtime/library_mono.js @@ -565,484 +565,52 @@ var MonoSupportLib = { }, }, - mono_wasm_get_exception_object: function() { - var exception_obj = MONO.active_exception; - MONO.active_exception = null; - return exception_obj ; - }, - - mono_wasm_get_call_stack: function() { - if (!this.mono_wasm_current_bp_id) - this.mono_wasm_current_bp_id = Module.cwrap ("mono_wasm_current_bp_id", 'number'); - if (!this.mono_wasm_enum_frames) - this.mono_wasm_enum_frames = Module.cwrap ("mono_wasm_enum_frames", null); - - var bp_id = this.mono_wasm_current_bp_id (); - this.active_frames = []; - this.mono_wasm_enum_frames (); - - var the_frames = this.active_frames; - this.active_frames = []; - return { - "breakpoint_id": bp_id, - "frames": the_frames, - }; - }, - - _fixup_name_value_objects: function (var_list) { - let out_list = []; - - var i = 0; - while (i < var_list.length) { - let o = var_list [i]; - const this_has_name = o.name !== undefined; - let next_has_value_or_get_set = false; - - if (i + 1 < var_list.length) { - const next = var_list [i+1]; - next_has_value_or_get_set = next.value !== undefined || next.get !== undefined || next.set !== undefined; - } - - if (!this_has_name) { - // insert the object as-is - // Eg. in case of locals, the names are added - // later - i ++; - } else if (next_has_value_or_get_set) { - // found a {name} followed by a {value/get} - o = Object.assign (o, var_list [i + 1]); - i += 2; - } else { - // missing value/get, so add a placeholder one - o.value = { - type: "symbol", - value: "", - description: "" - }; - i ++; - } - - out_list.push (o); - } - - return out_list; - }, - - _filter_automatic_properties: function (props, accessors_only=false) { - // Note: members in @props, have derived class members, followed by - // those from parent classes - - // Note: Auto-properties have backing fields, named with a special suffix. - // @props here will have the backing field, *and* the getter. - // - // But we want to return only one name/value pair: - // [name of the auto-property] = value of the backing field - - let getters = {}; - let all_fields_except_backing_fields = {}; - let backing_fields = {}; - - // Split props into the 3 groups - backing_fields, getters, and all_fields_except_backing_fields - props.forEach(p => { - if (p.name === undefined) { - console.debug(`Bug: Found a member with no name. Skipping it. p: ${JSON.stringify(p)}`); - return; - } - - if (p.name.endsWith('k__BackingField')) { - const auto_prop_name = p.name.replace ('k__BackingField', '') - .replace ('<', '') - .replace ('>', ''); - - // Only take the first one, as that is overriding others - if (!(auto_prop_name in backing_fields)) - backing_fields[auto_prop_name] = Object.assign(p, { name: auto_prop_name }); - - } else if (p.get !== undefined) { - // if p wasn't overridden by a getter or a field, - // from a more derived class - if (!(p.name in getters) && !(p.name in all_fields_except_backing_fields)) - getters[p.name] = p; - - } else if (!(p.name in all_fields_except_backing_fields)) { - all_fields_except_backing_fields[p.name] = p; - } - }); - - // Filter/merge backing fields, and getters - Object.values(backing_fields).forEach(backing_field => { - const auto_prop_name = backing_field.name; - const getter = getters[auto_prop_name]; - - if (getter === undefined) { - // backing field with no getter - // eg. when a field overrides/`new string foo=..` - // an autoproperty - return; - } - - if (auto_prop_name in all_fields_except_backing_fields) { - delete getters[auto_prop_name]; - } else if (getter.__args.owner_class === backing_field.__args.owner_class) { - // getter+backing_field are from the same class. - // Add the backing_field value as a field - all_fields_except_backing_fields[auto_prop_name] = backing_field; - - // .. and drop the auto-prop getter - delete getters[auto_prop_name]; + mono_wasm_add_dbg_command_received: function(res_ok, id, buffer, buffer_len) { + const assembly_data = new Uint8Array(Module.HEAPU8.buffer, buffer, buffer_len); + const base64String = MONO._base64Converter.toBase64StringImpl(assembly_data); + const buffer_obj = { + res_ok, + res: { + id, + value: base64String } - }); - - if (accessors_only) - return Object.values(getters); - - return Object.values(all_fields_except_backing_fields).concat(Object.values(getters)); - }, - - /** Given `dotnet:object:foo:bar`, - * returns { scheme:'object', value: 'foo:bar' } - * - * Given `dotnet:pointer:{ b: 3 }` - * returns { scheme:'object', value: '{b:3}`, o: {b:3} - * - * @param {string} idStr - * @param {boolean} [throwOnError=false] - * - * @returns {WasmId} - */ - _parse_object_id: function (idStr, throwOnError = false) { - if (idStr === undefined || idStr == "" || !idStr.startsWith ('dotnet:')) { - if (throwOnError) - throw new Error (`Invalid id: ${idStr}`); - - return undefined; } - - const [, scheme, ...rest] = idStr.split(':'); - let res = { - scheme, - value: rest.join (':'), - idStr, - o: {} - }; - - try { - res.o = JSON.parse(res.value); - // eslint-disable-next-line no-empty - } catch (e) {} - - return res; + MONO.commands_received = buffer_obj; }, - _resolve_member_by_name: function (base_object, base_name, expr_parts) { - if (base_object === undefined || base_object.value === undefined) - throw new Error(`Bug: base_object is undefined`); - - if (base_object.value.type === 'object' && base_object.value.subtype === 'null') - throw new ReferenceError(`Null reference: ${base_name} is null`); - - if (base_object.value.type !== 'object') - throw new ReferenceError(`'.' is only supported on non-primitive types. Failed on '${base_name}'`); - - if (expr_parts.length == 0) - throw new Error(`Invalid member access expression`);//FIXME: need the full expression here - - const root = expr_parts[0]; - const props = this.mono_wasm_get_details(base_object.value.objectId, {}); - let resObject = props.find(l => l.name == root); - if (resObject !== undefined) { - if (resObject.value === undefined && resObject.get !== undefined) - resObject = this._invoke_getter(base_object.value.objectId, root); - } - - if (resObject === undefined || expr_parts.length == 1) - return resObject; - else { - expr_parts.shift(); - return this._resolve_member_by_name(resObject, root, expr_parts); - } - }, - - mono_wasm_eval_member_access: function (scope, var_list, rootObjectId, expr) { - if (expr === undefined || expr.length == 0) - throw new Error(`expression argument required`); - - let parts = expr.split('.'); - if (parts.length == 0) - throw new Error(`Invalid member access expression: ${expr}`); - - const root = parts[0]; - - const locals = this.mono_wasm_get_variables(scope, var_list); - let rootObject = locals.find(l => l.name === root); - if (rootObject === undefined) { - // check `this` - const thisObject = locals.find(l => l.name == "this"); - if (thisObject === undefined) - throw new ReferenceError(`Could not find ${root} in locals, and no 'this' found.`); - - const thisProps = this.mono_wasm_get_details(thisObject.value.objectId, {}); - rootObject = thisProps.find(tp => tp.name == root); - if (rootObject === undefined) - throw new ReferenceError(`Could not find ${root} in locals, or in 'this'`); - - if (rootObject.value === undefined && rootObject.get !== undefined) - rootObject = this._invoke_getter(thisObject.value.objectId, root); - } - - parts.shift(); - - if (parts.length == 0) - return rootObject; - - if (rootObject === undefined || rootObject.value === undefined) - throw new Error(`Could not get a value for ${root}`); - - return this._resolve_member_by_name(rootObject, root, parts); - }, - - mono_wasm_set_variable_value: function (scope, index, name, newValue) { - console.debug (">> mono_wasm_set_variable_value " + name + " - " + newValue); - var ret = this._c_fn_table.mono_wasm_set_variable_on_frame_wrapper(scope, index, name, newValue); - if (ret == false) - throw new Error(`Could not get a value for ${name}`); - return ret; - }, - - /** - * @param {WasmId} id - * @returns {object[]} - */ - _get_vt_properties: function (id, args={}) { - let entry = this._get_id_props (id.idStr); - - if (entry === undefined || entry.members === undefined) { - if (!isNaN (id.o.containerId)) { - // We are expanding, so get *all* the members. - // Which ones to return based on @args, can be determined - // at the time of return - this._get_object_properties (id.o.containerId, { expandValueTypes: true }); - } else if (!isNaN (id.o.arrayId)) - this._get_array_values (id, Number (id.o.arrayIdx), 1, true); - else - throw new Error (`Invalid valuetype id (${id.idStr}). Can't get properties for it.`); - } - - // Let's try again - entry = this._get_id_props (id.idStr); - - if (entry !== undefined && entry.members !== undefined) { - if (args.accessorPropertiesOnly === true) - return entry.accessors; - - return entry.members; - } - - throw new Error (`Unknown valuetype id: ${id.idStr}. Failed to get properties for it.`); - }, - - /** - * - * @callback GetIdArgsCallback - * @param {object} var - * @param {number} idx - * @returns {object} - */ - - /** - * @param {object[]} vars - * @param {GetIdArgsCallback} getIdArgs - * @returns {object} - */ - _assign_vt_ids: function (vars, getIdArgs) + mono_wasm_send_dbg_command_with_parms: function (id, command_set, command, command_parameters, length, valtype, newvalue) { - vars.forEach ((v, i) => { - // we might not have a `.value`, like in case of getters which have a `.get` instead - const value = v.value; - if (value === undefined || !value.isValueType) - return; - - if (value.objectId !== undefined) - throw new Error (`Bug: Trying to assign valuetype id, but the var already has one: ${v}`); - - value.objectId = this._new_or_add_id_props ({ scheme: 'valuetype', idArgs: getIdArgs (v, i), props: value._props }); - delete value._props; - }); - - return vars; - }, - - // - // @var_list: [ { index: , name: }, .. ] - mono_wasm_get_variables: function(scope, var_list) { - const numBytes = var_list.length * Int32Array.BYTES_PER_ELEMENT; - const ptr = Module._malloc(numBytes); - let heapBytes = new Int32Array(Module.HEAP32.buffer, ptr, numBytes); - for (let i=0; i ({ containerId: this._async_method_objectId, fieldOffset: v.fieldOffset })); - - for (let i in res) { - const res_name = res [i].name; - if (this._async_method_objectId != 0) { - //Async methods are special in the way that local variables can be lifted to generated class fields - //value of "this" comes here either - if (res_name !== undefined && res_name.indexOf ('>') > 0) { - // For async methods, we get the names too, so use that - // ALTHOUGH, the name wouldn't have `<>` for method args - res [i].name = res_name.substring (1, res_name.indexOf ('>')); - } - } else if (res_name === undefined && var_list [i] !== undefined) { - // For non-async methods, we just have the var id, but we have the name - // from the caller - res [i].name = var_list [i].name; - } - } - - this._post_process_details(res); + throw new Error (`Failed on mono_wasm_invoke_method_debugger_agent_with_parms`); return res; }, - // Keep in sync with the flags in mini-wasm-debugger.c - _get_properties_args_to_gpflags: function (args) { - let gpflags =0; - /* - Disabled for now. Instead, we ask debugger.c to return - ~all~ the members, and then handle the filtering in mono.js . - - if (args.ownProperties) - gpflags |= 1; - if (args.accessorPropertiesOnly) - gpflags |= 2; - */ - if (args.expandValueTypes) - gpflags |= 4; - - return gpflags; - }, + mono_wasm_send_dbg_command: function (id, command_set, command, command_parameters) + { + const dataHeap = new Uint8Array (Module.HEAPU8.buffer, command_parameters, command_parameters.length); + dataHeap.set (new Uint8Array (this._base64_to_uint8 (command_parameters))); - /** - * @param {number} idNum - * @param {boolean} expandValueTypes - * @returns {object} - */ - _get_object_properties: function(idNum, args={}) { - let gpflags = this._get_properties_args_to_gpflags (args); + this._c_fn_table.mono_wasm_send_dbg_command_wrapper (id, command_set, command, dataHeap.byteOffset, command_parameters.length); - let { res_ok, res } = this.mono_wasm_get_object_properties_info (idNum, gpflags); + let { res_ok, res } = MONO.commands_received; if (!res_ok) - throw new Error (`Failed to get properties for ${idNum}`); - - res = MONO._filter_automatic_properties (res, args.accessorPropertiesOnly === true); - res = this._assign_vt_ids (res, v => ({ containerId: idNum, fieldOffset: v.fieldOffset })); - res = this._post_process_details (res); - + throw new Error (`Failed on mono_wasm_send_dbg_command`); return res; - }, - /** - * @param {WasmId} id - * @param {number} [startIdx=0] - * @param {number} [count=-1] - * @param {boolean} [expandValueTypes=false] - * @returns {object[]} - */ - _get_array_values: function (id, startIdx = 0, count = -1, expandValueTypes = false) { - if (isNaN (id.o.arrayId) || isNaN (startIdx)) - throw new Error (`Invalid array id: ${id.idStr}`); + }, - let gpflags = this._get_properties_args_to_gpflags({ expandValueTypes }); - let { res_ok, res } = this.mono_wasm_get_array_values_info (id.o.arrayId, startIdx, count, gpflags); + mono_wasm_get_dbg_command_info: function () + { + let { res_ok, res } = MONO.commands_received; if (!res_ok) - throw new Error (`Failed to get properties for array id ${id.idStr}`); - - res = this._assign_vt_ids (res, (_, i) => ({ arrayId: id.o.arrayId, arrayIdx: Number (startIdx) + i})); - - for (let i = 0; i < res.length; i ++) { - let value = res [i].value; - if (value.objectId !== undefined && value.objectId.startsWith("dotnet:pointer")) - this._new_or_add_id_props ({ objectId: value.objectId, props: { varName: `[${i}]` } }); - } - res = this._post_process_details (res); + throw new Error (`Failed on mono_wasm_get_dbg_command_info`); return res; }, - _post_process_details: function (details) { - if (details == undefined) - return {}; - - if (details.length > 0) - this._extract_and_cache_value_types(details); - - // remove __args added by add_properties_var - details.forEach(d => delete d.__args); - return details; - }, - - /** - * Gets the next id number to use for generating ids - * - * @returns {number} - */ - _next_id: function () { - return ++this._next_id_var; - }, - - _extract_and_cache_value_types: function (var_list) { - if (var_list == undefined || !Array.isArray (var_list) || var_list.length == 0) - return var_list; - - for (let i in var_list) { - let value = var_list [i].value; - if (value === undefined) - continue; - - if (value.objectId !== undefined && value.objectId.startsWith ("dotnet:pointer:")) { - let ptr_args = this._get_id_props (value.objectId); - if (ptr_args === undefined) - throw new Error (`Bug: Expected to find an entry for pointer id: ${value.objectId}`); - - // It might have been already set in some cases, like arrays - // where the name would be `0`, but we want `[0]` for pointers, - // so the deref would look like `*[0]` - ptr_args.varName = ptr_args.varName || var_list [i].name; - } - - if (value.type != "object" || value.isValueType != true || value.expanded != true) // undefined would also give us false - continue; - - if (value.members === undefined) { - // this could happen for valuetypes that maybe - // we were not able to describe, like `ref` parameters - // So, skip that - continue; - } - - // Generate objectId for expanded valuetypes - value.objectId = value.objectId || this._new_or_add_id_props ({ scheme: 'valuetype' }); - - this._extract_and_cache_value_types (value.members); - - const accessors = value.members.filter(m => m.get !== undefined); - const new_props = Object.assign ({ members: value.members, accessors }, value.__extra_vt_props); - - this._new_or_add_id_props ({ objectId: value.objectId, props: new_props }); - delete value.members; - delete value.__extra_vt_props; - } - - return var_list; - }, - _get_cfo_res_details: function (objectId, args) { if (!(objectId in this._call_function_res_cache)) throw new Error(`Could not find any object with id ${objectId}`); @@ -1101,116 +669,8 @@ var MonoSupportLib = { return { __value_as_json_string__: JSON.stringify (res_details) }; }, - /** - * Generates a new id, and a corresponding entry for associated properties - * like `dotnet:pointer:{ a: 4 }` - * The third segment of that `{a:4}` is the idArgs parameter - * - * Only `scheme` or `objectId` can be set. - * if `scheme`, then a new id is generated, and it's properties set - * if `objectId`, then it's properties are updated - * - * @param {object} args - * @param {string} [args.scheme=undefined] scheme second part of `dotnet:pointer:..` - * @param {string} [args.objectId=undefined] objectId - * @param {object} [args.idArgs={}] The third segment of the objectId - * @param {object} [args.props={}] Properties for the generated id - * - * @returns {string} generated/updated id string - */ - _new_or_add_id_props: function ({ scheme = undefined, objectId = undefined, idArgs = {}, props = {} }) { - if (scheme === undefined && objectId === undefined) - throw new Error (`Either scheme or objectId must be given`); - - if (scheme !== undefined && objectId !== undefined) - throw new Error (`Both scheme, and objectId cannot be given`); - - if (objectId !== undefined && Object.entries (idArgs).length > 0) - throw new Error (`Both objectId, and idArgs cannot be given`); - - if (Object.entries (idArgs).length == 0) { - // We want to generate a new id, only if it doesn't have other - // attributes that it can use to uniquely identify. - // Eg, we don't do this for `dotnet:valuetype:{containerId:4, fieldOffset: 24}` - idArgs.num = this._next_id (); - } - - let idStr; - if (objectId !== undefined) { - idStr = objectId; - const old_props = this._id_table [idStr]; - if (old_props === undefined) - throw new Error (`ObjectId not found in the id table: ${idStr}`); - - this._id_table [idStr] = Object.assign (old_props, props); - } else { - idStr = `dotnet:${scheme}:${JSON.stringify (idArgs)}`; - this._id_table [idStr] = props; - } - - return idStr; - }, - - /** - * @param {string} objectId - * @returns {object} - */ - _get_id_props: function (objectId) { - return this._id_table [objectId]; - }, - - _get_deref_ptr_value: function (objectId) { - const ptr_args = this._get_id_props (objectId); - if (ptr_args === undefined) - throw new Error (`Unknown pointer id: ${objectId}`); - - if (ptr_args.ptr_addr == 0 || ptr_args.klass_addr == 0) - throw new Error (`Both ptr_addr and klass_addr need to be non-zero, to dereference a pointer. objectId: ${objectId}`); - - const value_addr = new DataView (Module.HEAPU8.buffer).getUint32 (ptr_args.ptr_addr, /* littleEndian */ true); - let { res_ok, res } = this.mono_wasm_get_deref_ptr_value_info (value_addr, ptr_args.klass_addr); - if (!res_ok) - throw new Error (`Failed to dereference pointer ${objectId}`); - - if (res.length > 0) { - if (ptr_args.varName === undefined) - throw new Error (`Bug: no varName found for the pointer. objectId: ${objectId}`); - - res [0].name = `*${ptr_args.varName}`; - } - - res = this._post_process_details (res); - return res; - }, - mono_wasm_get_details: function (objectId, args={}) { - let id = this._parse_object_id (objectId, true); - - switch (id.scheme) { - case "object": { - if (isNaN (id.value)) - throw new Error (`Invalid objectId: ${objectId}. Expected a numeric id.`); - - args.expandValueTypes = false; - return this._get_object_properties(id.value, args); - } - - case "array": - return this._get_array_values (id); - - case "valuetype": - return this._get_vt_properties(id, args); - - case "cfo_res": - return this._get_cfo_res_details (objectId, args); - - case "pointer": { - return this._get_deref_ptr_value (objectId); - } - - default: - throw new Error(`Unknown object id format: ${objectId}`); - } + return this._get_cfo_res_details (`dotnet:cfo_res:${objectId}`, args); }, _cache_call_function_res: function (obj) { @@ -1224,105 +684,33 @@ var MonoSupportLib = { delete this._cache_call_function_res[objectId]; }, - /** - * @param {string} objectIdStr objectId - * @param {string} name property name - * @returns {object} return value - */ - _invoke_getter: function (objectIdStr, name) { - const id = this._parse_object_id (objectIdStr); - if (id === undefined) - throw new Error (`Invalid object id: ${objectIdStr}`); - - let getter_res; - if (id.scheme == 'object') { - if (isNaN (id.o) || id.o < 0) - throw new Error (`Invalid object id: ${objectIdStr}`); - - let { res_ok, res } = this.mono_wasm_invoke_getter_on_object_info (id.o, name); - if (!res_ok) - throw new Error (`Invoking getter on ${objectIdStr} failed`); - - getter_res = res; - } else if (id.scheme == 'valuetype') { - const id_props = this._get_id_props (objectIdStr); - if (id_props === undefined) - throw new Error (`Unknown valuetype id: ${objectIdStr}`); - - if (typeof id_props.value64 !== 'string' || isNaN (id_props.klass)) - throw new Error (`Bug: Cannot invoke getter on ${objectIdStr}, because of missing or invalid klass/value64 fields. idProps: ${JSON.stringify (id_props)}`); - - const dataPtr = Module._malloc (id_props.value64.length); - const dataHeap = new Uint8Array (Module.HEAPU8.buffer, dataPtr, id_props.value64.length); - dataHeap.set (new Uint8Array (this._base64_to_uint8 (id_props.value64))); - - let { res_ok, res } = this.mono_wasm_invoke_getter_on_value_info (dataHeap.byteOffset, id_props.klass, name); - Module._free (dataHeap.byteOffset); - - if (!res_ok) { - console.debug (`Invoking getter on valuetype ${objectIdStr}, with props: ${JSON.stringify (id_props)} failed`); - throw new Error (`Invoking getter on valuetype ${objectIdStr} failed`); - } - getter_res = res; - } else { - throw new Error (`Only object, and valuetypes supported for getters, id: ${objectIdStr}`); - } - - getter_res = MONO._post_process_details (getter_res); - return getter_res.length > 0 ? getter_res [0] : {}; - }, - - /** - * @param {string} objectIdStr objectId - * @param {string} name property name - * @returns {object} return true if it works and false if it doesn't - */ - _set_value_on_object: function (objectIdStr, name, newvalue) { - const id = this._parse_object_id (objectIdStr); - if (id === undefined) - throw new Error (`Invalid object id: ${objectIdStr}`); - - let setter_res; - if (id.scheme == 'object') { - if (isNaN (id.o) || id.o < 0) - throw new Error (`Invalid object id: ${objectIdStr}`); - - var ret = this._c_fn_table.mono_wasm_set_value_on_object_wrapper (id.o, name, newvalue); - if (!ret) - throw new Error (`Invoking setter on ${objectIdStr} failed`); - - setter_res = ret; - } - else - throw new Error (`Only object is supported for setters, id: ${objectIdStr}`); - return setter_res; - }, - - _create_proxy_from_object_id: function (objectId) { - const details = this.mono_wasm_get_details(objectId); - + _create_proxy_from_object_id: function (objectId, details) { if (objectId.startsWith ('dotnet:array:')) - return details.map (p => p.value); + { + let ret = details.map (p => p.value); + return ret; + } let proxy = {}; Object.keys (details).forEach (p => { var prop = details [p]; if (prop.get !== undefined) { - // TODO: `set` - Object.defineProperty (proxy, prop.name, - { get () { return MONO._invoke_getter (objectId, prop.name); } } + { get () { return MONO.mono_wasm_send_dbg_command(-1, prop.get.commandSet, prop.get.command, prop.get.buffer, prop.get.length); }, + set: function (newValue) { MONO.mono_wasm_send_dbg_command_with_parms(-1, prop.set.commandSet, prop.set.command, prop.set.buffer, prop.set.length, prop.set.valtype, newValue); return MONO.commands_received.res_ok;}} + ); + } else if (prop.set !== undefined ){ + Object.defineProperty (proxy, + prop.name, + { get () { return prop.value; }, + set: function (newValue) { MONO.mono_wasm_send_dbg_command_with_parms(-1, prop.set.commandSet, prop.set.command, prop.set.buffer, prop.set.length, prop.set.valtype, newValue); return MONO.commands_received.res_ok;}} ); } else { proxy [prop.name] = prop.value; } }); - - const handler1 = { - set (obj, prop, newValue) {return MONO._set_value_on_object (objectId, prop, newValue.toString());}, - }; - return new Proxy(proxy, handler1); + return proxy; }, mono_wasm_call_function_on: function (request) { @@ -1330,6 +718,7 @@ var MonoSupportLib = { throw new Error (`"arguments" should be an array, but was ${request.arguments}`); const objId = request.objectId; + const details = request.details; let proxy; if (objId.startsWith ('dotnet:cfo_res:')) { @@ -1338,7 +727,7 @@ var MonoSupportLib = { else throw new Error (`Unknown object id ${objId}`); } else { - proxy = this._create_proxy_from_object_id (objId); + proxy = this._create_proxy_from_object_id (objId, details); } const fn_args = request.arguments != undefined ? request.arguments.map(a => JSON.stringify(a.value)) : []; @@ -1348,22 +737,19 @@ var MonoSupportLib = { if (fn_res === undefined) return { type: "undefined" }; - if (fn_res === null || (fn_res.subtype === 'null' && fn_res.value === undefined)) - return fn_res; - - // primitive type if (Object (fn_res) !== fn_res) - return fn_res; - - // return .value, if it is a primitive type - if (fn_res.value !== undefined && Object (fn_res.value.value) !== fn_res.value.value) - return fn_res.value; + { + if (typeof(fn_res) == "object" && fn_res == null) + return { type: typeof(fn_res), subtype: `${fn_res}`, value: null }; + return { type: typeof(fn_res), description: `${fn_res}`, value: `${fn_res}`}; + } - if (request.returnByValue) + if (request.returnByValue && fn_res.subtype == undefined) return {type: "object", value: fn_res}; - - const fn_res_id = this._cache_call_function_res (fn_res); if (Object.getPrototypeOf (fn_res) == Array.prototype) { + + const fn_res_id = this._cache_call_function_res (fn_res); + return { type: "object", subtype: "array", @@ -1371,9 +757,15 @@ var MonoSupportLib = { description: `Array(${fn_res.length})`, objectId: fn_res_id }; - } else { - return { type: "object", className: "Object", description: "Object", objectId: fn_res_id }; } + if (fn_res.value !== undefined || fn_res.subtype !== undefined) { + return fn_res; + } + + if (fn_res == proxy) + return { type: "object", className: "Object", description: "Object", objectId: objId }; + const fn_res_id = this._cache_call_function_res (fn_res); + return { type: "object", className: "Object", description: "Object", objectId: fn_res_id }; }, _clear_per_step_state: function () { @@ -1385,31 +777,6 @@ var MonoSupportLib = { this._clear_per_step_state (); }, - mono_wasm_start_single_stepping: function (kind) { - console.debug (">> mono_wasm_start_single_stepping " + kind); - if (!this.mono_wasm_setup_single_step) - this.mono_wasm_setup_single_step = Module.cwrap ("mono_wasm_setup_single_step", 'number', [ 'number']); - - this._clear_per_step_state (); - - return this.mono_wasm_setup_single_step (kind); - }, - - mono_wasm_set_pause_on_exceptions: function (state) { - if (!this.mono_wasm_pause_on_exceptions) - this.mono_wasm_pause_on_exceptions = Module.cwrap ("mono_wasm_pause_on_exceptions", 'number', [ 'number']); - var state_enum = 0; - switch (state) { - case 'uncaught': - state_enum = 1; //EXCEPTION_MODE_UNCAUGHT - break; - case 'all': - state_enum = 2; //EXCEPTION_MODE_ALL - break; - } - return this.mono_wasm_pause_on_exceptions (state_enum); - }, - mono_wasm_detach_debugger: function () { if (!this.mono_wasm_set_is_debugger_attached) this.mono_wasm_set_is_debugger_attached = Module.cwrap ('mono_wasm_set_is_debugger_attached', 'void', ['bool']); @@ -1461,14 +828,9 @@ var MonoSupportLib = { this._call_function_res_cache = {}; this._c_fn_table = {}; - this._register_c_var_fn ('mono_wasm_get_object_properties', 'bool', [ 'number', 'number' ]); - this._register_c_var_fn ('mono_wasm_get_array_values', 'bool', [ 'number', 'number', 'number', 'number' ]); - this._register_c_var_fn ('mono_wasm_invoke_getter_on_object', 'bool', [ 'number', 'string' ]); - this._register_c_var_fn ('mono_wasm_invoke_getter_on_value', 'bool', [ 'number', 'number', 'string' ]); - this._register_c_var_fn ('mono_wasm_get_local_vars', 'bool', [ 'number', 'number', 'number']); - this._register_c_var_fn ('mono_wasm_get_deref_ptr_value', 'bool', [ 'number', 'number']); - this._register_c_fn ('mono_wasm_set_value_on_object', 'bool', [ 'number', 'string', 'string' ]); - this._register_c_fn ('mono_wasm_set_variable_on_frame', 'bool', [ 'number', 'number', 'string', 'string']); + this._register_c_fn ('mono_wasm_send_dbg_command', 'bool', [ 'number', 'number', 'number', 'number', 'number' ]); + this._register_c_fn ('mono_wasm_send_dbg_command_with_parms', 'bool', [ 'number', 'number', 'number', 'number', 'number', 'number', 'string' ]); + // DO NOT REMOVE - magic debugger init function if (globalThis.dotnetDebugger) debugger; @@ -1476,20 +838,6 @@ var MonoSupportLib = { console.debug ("mono_wasm_runtime_ready", "fe00e07a-5519-4dfe-b35a-f867dbaf2e28"); }, - mono_wasm_set_breakpoint: function (assembly, method_token, il_offset) { - if (!this.mono_wasm_set_bp) - this.mono_wasm_set_bp = Module.cwrap ('mono_wasm_set_breakpoint', 'number', ['string', 'number', 'number']); - - return this.mono_wasm_set_bp (assembly, method_token, il_offset) - }, - - mono_wasm_remove_breakpoint: function (breakpoint_id) { - if (!this.mono_wasm_del_bp) - this.mono_wasm_del_bp = Module.cwrap ('mono_wasm_remove_breakpoint', 'number', ['number']); - - return this.mono_wasm_del_bp (breakpoint_id); - }, - // Set environment variable NAME to VALUE // Should be called before mono_load_runtime_and_bcl () in most cases mono_wasm_setenv: function (name, value) { @@ -2000,78 +1348,6 @@ var MonoSupportLib = { return MONO.loaded_assets; }, - mono_wasm_clear_all_breakpoints: function() { - if (!this.mono_clear_bps) - this.mono_clear_bps = Module.cwrap ('mono_wasm_clear_all_breakpoints', null); - - this.mono_clear_bps (); - }, - - mono_wasm_add_null_var: function(className) - { - let fixed_class_name = MONO._mono_csharp_fixup_class_name(Module.UTF8ToString (className)); - if (!fixed_class_name) { - // Eg, when a @className is passed from js itself, like - // mono_wasm_add_null_var ("string") - fixed_class_name = className; - } - MONO.var_info.push ({value: { - type: "object", - className: fixed_class_name, - description: fixed_class_name, - subtype: "null" - }}); - }, - - _mono_wasm_add_string_var: function(var_value) { - if (var_value === 0) { - MONO.mono_wasm_add_null_var ("string"); - return; - } - - MONO.var_info.push({ - value: { - type: "string", - value: var_value, - description: var_value - } - }); - }, - - _mono_wasm_add_getter_var: function(className) { - const fixed_class_name = MONO._mono_csharp_fixup_class_name (className); - var name; - if (MONO.var_info.length > 0) - name = MONO.var_info [MONO.var_info.length - 1].name; - name = (name === undefined) ? "" : name; - - MONO.var_info.push({ - get: { - className: "Function", - description: `get ${name} () {}`, - type: "function", - } - }); - }, - - _mono_wasm_add_array_var: function(className, objectId, length) { - const fixed_class_name = MONO._mono_csharp_fixup_class_name(className); - if (objectId == 0) { - MONO.mono_wasm_add_null_var (fixed_class_name); - return; - } - - MONO.var_info.push({ - value: { - type: "object", - subtype: "array", - className: fixed_class_name, - description: `${fixed_class_name}(${length})`, - objectId: this._new_or_add_id_props ({ scheme: 'array', idArgs: { arrayId: objectId } }) - } - }); - }, - // FIXME: improve _base64_to_uint8: function (base64String) { const byteCharacters = atob (base64String); @@ -2083,215 +1359,6 @@ var MonoSupportLib = { return new Uint8Array (byteNumbers); }, - _begin_value_type_var: function(className, args) { - if (args === undefined || (typeof args !== 'object')) { - console.debug (`_begin_value_type_var: Expected an args object`); - return; - } - - const fixed_class_name = MONO._mono_csharp_fixup_class_name(className); - const toString = args.toString; - const base64String = btoa (String.fromCharCode (...new Uint8Array (Module.HEAPU8.buffer, args.value_addr, args.value_size))); - const vt_obj = { - value: { - type : "object", - className : fixed_class_name, - description : (toString === 0 ? fixed_class_name: Module.UTF8ToString (toString)), - expanded : true, - isValueType : true, - __extra_vt_props: { klass: args.klass, value64: base64String }, - members : [] - } - }; - if (MONO._vt_stack.length == 0) - MONO._old_var_info = MONO.var_info; - - MONO.var_info = vt_obj.value.members; - MONO._vt_stack.push (vt_obj); - }, - - _end_value_type_var: function() { - let top_vt_obj_popped = MONO._vt_stack.pop (); - top_vt_obj_popped.value.members = MONO._filter_automatic_properties ( - MONO._fixup_name_value_objects (top_vt_obj_popped.value.members)); - - if (MONO._vt_stack.length == 0) { - MONO.var_info = MONO._old_var_info; - MONO.var_info.push(top_vt_obj_popped); - } else { - var top_obj = MONO._vt_stack [MONO._vt_stack.length - 1]; - top_obj.value.members.push (top_vt_obj_popped); - MONO.var_info = top_obj.value.members; - } - }, - - _add_valuetype_unexpanded_var: function(className, args) { - if (args === undefined || (typeof args !== 'object')) { - console.debug (`_add_valuetype_unexpanded_var: Expected an args object`); - return; - } - - const fixed_class_name = MONO._mono_csharp_fixup_class_name (className); - const toString = args.toString; - - MONO.var_info.push ({ - value: { - type: "object", - className: fixed_class_name, - description: (toString === 0 ? fixed_class_name : Module.UTF8ToString (toString)), - isValueType: true - } - }); - }, - - mono_wasm_add_properties_var: function (name, args) { - if (typeof args !== 'object') - args = { field_offset: args }; - - if (args.owner_class !== undefined && args.owner_class !== 0) - args.owner_class = Module.UTF8ToString(args.owner_class); - - let name_obj = { - name: Module.UTF8ToString (name), - fieldOffset: args.field_offset, - __args: args - }; - if (args.is_own) - name_obj.isOwn = true; - - MONO.var_info.push(name_obj); - }, - - mono_wasm_add_typed_value: function (type, str_value, value) { - let type_str = type; - if (typeof type != 'string') - type_str = Module.UTF8ToString (type); - - if (str_value !== 0) - str_value = Module.UTF8ToString (str_value); - - switch (type_str) { - case "bool": { - const v = value != 0; - MONO.var_info.push ({ - value: { - type: "boolean", - value: v, - description: v.toString () - }, - writable:true - }); - break; - } - - case "char": { - const v = `${value} '${String.fromCharCode (value)}'`; - MONO.var_info.push ({ - value: { - type: "symbol", - value: v, - description: v - }, - writable:true - }); - break; - } - - case "number": - MONO.var_info.push ({ - value: { - type: "number", - value: value, - description: '' + value - }, - writable:true - }); - break; - - case "string": - MONO._mono_wasm_add_string_var (str_value); - break; - - case "getter": - MONO._mono_wasm_add_getter_var (str_value); - break; - - case "array": - MONO._mono_wasm_add_array_var (str_value, value.objectId, value.length); - break; - - case "begin_vt": - MONO._begin_value_type_var (str_value, value); - break; - - case "end_vt": - MONO._end_value_type_var (); - break; - - case "unexpanded_vt": - MONO._add_valuetype_unexpanded_var (str_value, value); - break; - - case "pointer": { - const fixed_value_str = MONO._mono_csharp_fixup_class_name (str_value); - if (value.klass_addr == 0 || value.ptr_addr == 0 || fixed_value_str.startsWith ('(void*')) { - // null or void*, which we can't deref - MONO.var_info.push({ - value: { - type: "symbol", - value: fixed_value_str, - description: fixed_value_str - } - }); - } else { - MONO.var_info.push({ - value: { - type: "object", - className: fixed_value_str, - description: fixed_value_str, - objectId: this._new_or_add_id_props ({ scheme: 'pointer', props: value }) - } - }); - } - } - break; - - case "symbol": { - if (typeof value === 'object' && value.isClassName) - str_value = MONO._mono_csharp_fixup_class_name (str_value); - - MONO.var_info.push ({ - value: { - type: "symbol", - value: str_value, - description: str_value - } - }); - } - break; - - default: { - const msg = `'${str_value}' ${value}`; - - MONO.var_info.push ({ - value: { - type: "symbol", - value: msg, - description: msg - } - }); - break; - } - } - }, - - _mono_csharp_fixup_class_name: function(className) - { - // Fix up generic names like Foo`2 to Foo - // and nested class names like Foo/Bar to Foo.Bar - return className.replace(/\//g, '.').replace(/`\d+/g, ''); - }, - mono_wasm_load_data_archive: function (data, prefix) { if (data.length < 8) return false; @@ -2392,132 +1459,6 @@ var MonoSupportLib = { } } }, - - mono_wasm_add_typed_value: function (type, str_value, value) { - MONO.mono_wasm_add_typed_value (type, str_value, value); - }, - - mono_wasm_add_properties_var: function(name, args) { - MONO.mono_wasm_add_properties_var (name, args); - }, - - mono_wasm_set_is_async_method: function(objectId) { - MONO._async_method_objectId = objectId; - }, - - mono_wasm_add_enum_var: function(className, members, value) { - // FIXME: flags - // - - // group0: Monday:0 - // group1: Monday - // group2: 0 - const re = new RegExp (`[,]?([^,:]+):(${value}(?=,)|${value}$)`, 'g') - const members_str = Module.UTF8ToString (members); - - const match = re.exec(members_str); - const member_name = match == null ? ('' + value) : match [1]; - - const fixed_class_name = MONO._mono_csharp_fixup_class_name(Module.UTF8ToString (className)); - MONO.var_info.push({ - value: { - type: "object", - className: fixed_class_name, - description: member_name, - isEnum: true - } - }); - }, - - mono_wasm_add_array_item: function(position) { - MONO.var_info.push({ - name: `${position}` - }); - }, - - mono_wasm_add_obj_var: function(className, toString, objectId) { - if (objectId == 0) { - MONO.mono_wasm_add_null_var (className); - return; - } - - const fixed_class_name = MONO._mono_csharp_fixup_class_name(Module.UTF8ToString (className)); - MONO.var_info.push({ - value: { - type: "object", - className: fixed_class_name, - description: (toString === 0 ? fixed_class_name : Module.UTF8ToString (toString)), - objectId: "dotnet:object:"+ objectId, - } - }); - }, - - /* - * @className, and @targetName are in the following format: - * - * :[]: - */ - mono_wasm_add_func_var: function (className, targetName, objectId) { - if (objectId == 0) { - MONO.mono_wasm_add_null_var ( - MONO._mono_csharp_fixup_class_name (Module.UTF8ToString (className))); - return; - } - - function args_to_sig (args_str) { - var parts = args_str.split (":"); - // TODO: min length = 3? - parts = parts.map (a => MONO._mono_csharp_fixup_class_name (a)); - - // method name at the end - var method_name = parts.pop (); - - // ret type at the beginning - var ret_sig = parts [0]; - var args_sig = parts.splice (1).join (', '); - return `${ret_sig} ${method_name} (${args_sig})`; - } - let tgt_sig; - if (targetName != 0) - tgt_sig = args_to_sig (Module.UTF8ToString (targetName)); - - const type_name = MONO._mono_csharp_fixup_class_name (Module.UTF8ToString (className)); - if (tgt_sig === undefined) - tgt_sig = type_name; - - if (objectId == -1 || targetName === 0) { - // Target property - MONO.var_info.push ({ - value: { - type: "symbol", - value: tgt_sig, - description: tgt_sig, - } - }); - } else { - MONO.var_info.push ({ - value: { - type: "object", - className: type_name, - description: tgt_sig, - objectId: "dotnet:object:" + objectId, - } - }); - } - }, - - mono_wasm_add_frame: function(il, method, frame_id, assembly_name, method_full_name) { - var parts = Module.UTF8ToString (method_full_name).split (":", 2); - MONO.active_frames.push( { - il_pos: il, - method_token: method, - assembly_name: Module.UTF8ToString (assembly_name), - // Extract just the method name from `{class_name}:{method_name}` - method_name: parts [parts.length - 1], - frame_id - }); - }, - schedule_background_exec: function () { ++MONO.pump_count; if (typeof globalThis.setTimeout === 'function') { @@ -2541,21 +1482,11 @@ var MonoSupportLib = { } }, - mono_wasm_fire_bp: function () { + mono_wasm_fire_debugger_agent_message: function () { // eslint-disable-next-line no-debugger debugger; }, - mono_wasm_fire_exception: function (exception_id, message, class_name, uncaught) { - MONO.active_exception = { - exception_id: exception_id, - message : Module.UTF8ToString (message), - class_name : Module.UTF8ToString (class_name), - uncaught : uncaught - }; - debugger; - }, - mono_wasm_asm_loaded: function (assembly_name, assembly_ptr, assembly_len, pdb_ptr, pdb_len) { // Only trigger this codepath for assemblies loaded after app is ready if (MONO.mono_wasm_runtime_is_ready !== true) From 93f407d231e31687decdc1f5b7f466af2fb94737 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksey=20Kliger=20=28=CE=BBgeek=29?= Date: Wed, 23 Jun 2021 20:13:37 -0400 Subject: [PATCH 090/107] [wasm] Build static components; include hot_reload in runtime (#54568) * [wasm] Build static components; include hot_reload in runtime Workaround until https://github.com/dotnet/runtime/issues/54565 is fixed Build the runtime always with support for hot_reload, and without diagnostics_tracing * Update wasm.proj * Add a browser functional test for hot reload Just check that the capabilities are non-empty which is a good proxy for hot reload being enabled in the runtime. * Turn off trimming for hot reload functional test * Disable test on browser AOT * fix whitespace Co-authored-by: Thays Grazia --- ...tadata.ApplyUpdate.Test.MethodBody1.csproj | 1 + .../tests/ApplyUpdateTest.cs | 1 + src/mono/mono.proj | 1 - src/mono/wasm/Makefile | 8 ++ src/mono/wasm/wasm.proj | 3 + .../ApplyUpdateReferencedAssembly.csproj | 30 +++++++ .../MethodBody1.cs | 11 +++ .../MethodBody1_v1.cs | 11 +++ .../MethodBody1_v2.cs | 11 +++ .../deltascript.json | 7 ++ .../WebAssembly/Browser/HotReload/Program.cs | 81 +++++++++++++++++++ .../WebAssembly.Browser.HotReload.Test.csproj | 52 ++++++++++++ .../WebAssembly/Browser/HotReload/index.html | 55 +++++++++++++ .../WebAssembly/Browser/HotReload/runtime.js | 47 +++++++++++ 14 files changed, 318 insertions(+), 1 deletion(-) create mode 100644 src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/ApplyUpdateReferencedAssembly.csproj create mode 100644 src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/MethodBody1.cs create mode 100644 src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/MethodBody1_v1.cs create mode 100644 src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/MethodBody1_v2.cs create mode 100644 src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/deltascript.json create mode 100644 src/tests/FunctionalTests/WebAssembly/Browser/HotReload/Program.cs create mode 100644 src/tests/FunctionalTests/WebAssembly/Browser/HotReload/WebAssembly.Browser.HotReload.Test.csproj create mode 100644 src/tests/FunctionalTests/WebAssembly/Browser/HotReload/index.html create mode 100644 src/tests/FunctionalTests/WebAssembly/Browser/HotReload/runtime.js diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1.csproj b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1.csproj index f05397e884c83..57ba4f3ec5298 100644 --- a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1.csproj +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1.csproj @@ -4,6 +4,7 @@ $(NetCoreAppCurrent) true deltascript.json + true diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs b/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs index e533377e673ac..2a953d19eece8 100644 --- a/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs @@ -18,6 +18,7 @@ namespace System.Reflection.Metadata public class ApplyUpdateTest { [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54617", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] void StaticMethodBodyUpdate() { ApplyUpdateUtil.TestCase(static () => diff --git a/src/mono/mono.proj b/src/mono/mono.proj index e5d4bb2970aa3..e459f87217716 100644 --- a/src/mono/mono.proj +++ b/src/mono/mono.proj @@ -294,7 +294,6 @@ <_MonoCMakeArgs Include="-DENABLE_INTERP_LIB=1"/> <_MonoCMakeArgs Include="-DDISABLE_ICALL_TABLES=1"/> <_MonoCMakeArgs Include="-DDISABLE_CRASH_REPORTING=1"/> - <_MonoCMakeArgs Include="-DDISABLE_COMPONENTS=1"/> <_MonoCMakeArgs Include="-DENABLE_ICALL_EXPORT=1"/> <_MonoCMakeArgs Include="-DENABLE_LAZY_GC_THREAD_CREATION=1"/> <_MonoCMakeArgs Include="-DENABLE_LLVM_RUNTIME=1"/> diff --git a/src/mono/wasm/Makefile b/src/mono/wasm/Makefile index 9f6d055511193..86288cd674e70 100644 --- a/src/mono/wasm/Makefile +++ b/src/mono/wasm/Makefile @@ -48,12 +48,20 @@ provision-wasm: .stamp-wasm-install-and-select-$(EMSCRIPTEN_VERSION) @echo "----------------------------------------------------------" @echo "Installed emsdk into EMSDK_PATH=$(TOP)/src/mono/wasm/emsdk" +# FIXME: When https://github.com/dotnet/runtime/issues/54565 is fixed, and the WasmApp targets are updated to use mono runtime components, remove this +MONO_COMPONENT_LIBS= \ + $(MONO_BIN_DIR)/libmono-component-hot_reload-static.a \ + $(MONO_BIN_DIR)/libmono-component-diagnostics_tracing-stub-static.a + MONO_OBJ_DIR=$(OBJDIR)/mono/Browser.wasm.$(CONFIG) MONO_INCLUDE_DIR=$(MONO_BIN_DIR)/include/mono-2.0 BUILDS_OBJ_DIR=$(MONO_OBJ_DIR)/wasm +# libmonosgen-2.0 is in MONO_LIBS twice because the components and the runtime are depend on each other MONO_LIBS = \ $(MONO_BIN_DIR)/libmono-ee-interp.a \ $(MONO_BIN_DIR)/libmonosgen-2.0.a \ + $(MONO_COMPONENT_LIBS) \ + $(MONO_BIN_DIR)/libmonosgen-2.0.a \ $(MONO_BIN_DIR)/libmono-ilgen.a \ $(MONO_BIN_DIR)/libmono-icall-table.a \ $(MONO_BIN_DIR)/libmono-profiler-aot.a \ diff --git a/src/mono/wasm/wasm.proj b/src/mono/wasm/wasm.proj index 489d88259f5f3..ffc2fb92825ba 100644 --- a/src/mono/wasm/wasm.proj +++ b/src/mono/wasm/wasm.proj @@ -189,6 +189,8 @@ + + diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/ApplyUpdateReferencedAssembly.csproj b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/ApplyUpdateReferencedAssembly.csproj new file mode 100644 index 0000000000000..46e260f24e155 --- /dev/null +++ b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/ApplyUpdateReferencedAssembly.csproj @@ -0,0 +1,30 @@ + + + true + deltascript.json + library + false + true + + false + true + + false + false + + + + + + + + + + + diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/MethodBody1.cs b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/MethodBody1.cs new file mode 100644 index 0000000000000..9e98604b921d2 --- /dev/null +++ b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/MethodBody1.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace ApplyUpdateReferencedAssembly +{ + public class MethodBody1 { + public static string StaticMethod1 () { + return "OLD STRING"; + } + } +} diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/MethodBody1_v1.cs b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/MethodBody1_v1.cs new file mode 100644 index 0000000000000..4aab1e81dade0 --- /dev/null +++ b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/MethodBody1_v1.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace ApplyUpdateReferencedAssembly +{ + public class MethodBody1 { + public static string StaticMethod1 () { + return "NEW STRING"; + } + } +} diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/MethodBody1_v2.cs b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/MethodBody1_v2.cs new file mode 100644 index 0000000000000..83f0142e55f89 --- /dev/null +++ b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/MethodBody1_v2.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace ApplyUpdateReferencedAssembly +{ + public class MethodBody1 { + public static string StaticMethod1 () { + return "NEWEST STRING"; + } + } +} diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/deltascript.json b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/deltascript.json new file mode 100644 index 0000000000000..8e738364bc747 --- /dev/null +++ b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/deltascript.json @@ -0,0 +1,7 @@ +{ + "changes": [ + {"document": "MethodBody1.cs", "update": "MethodBody1_v1.cs"}, + {"document": "MethodBody1.cs", "update": "MethodBody1_v2.cs"}, + ] +} + diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/Program.cs b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/Program.cs new file mode 100644 index 0000000000000..cb005b090887d --- /dev/null +++ b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/Program.cs @@ -0,0 +1,81 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; + +namespace Sample +{ + public class Test + { + public static void Main(string[] args) + { + Console.WriteLine ("Hello, World!"); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int TestMeaning() + { + const int success = 42; + const int failure = 1; + + var ty = typeof(System.Reflection.Metadata.AssemblyExtensions); + var mi = ty.GetMethod("GetApplyUpdateCapabilities", BindingFlags.NonPublic | BindingFlags.Static, Array.Empty()); + + if (mi == null) + return failure; + + var caps = mi.Invoke(null, null) as string; + + if (String.IsNullOrEmpty(caps)) + return failure; + + var assm = typeof (ApplyUpdateReferencedAssembly.MethodBody1).Assembly; + + var r = ApplyUpdateReferencedAssembly.MethodBody1.StaticMethod1(); + if ("OLD STRING" != r) + return failure; + + ApplyUpdate(assm); + + r = ApplyUpdateReferencedAssembly.MethodBody1.StaticMethod1(); + if ("NEW STRING" != r) + return failure; + + ApplyUpdate(assm); + + r = ApplyUpdateReferencedAssembly.MethodBody1.StaticMethod1(); + if ("NEWEST STRING" != r) + return failure; + + return success; + } + + private static System.Collections.Generic.Dictionary assembly_count = new(); + + internal static void ApplyUpdate (System.Reflection.Assembly assm) + { + int count; + if (!assembly_count.TryGetValue(assm, out count)) + count = 1; + else + count++; + assembly_count [assm] = count; + + /* FIXME WASM: Location is empty on wasm. Make up a name based on Name */ + string basename = assm.Location; + if (basename == "") + basename = assm.GetName().Name + ".dll"; + Console.Error.WriteLine($"Apply Delta Update for {basename}, revision {count}"); + + string dmeta_name = $"{basename}.{count}.dmeta"; + string dil_name = $"{basename}.{count}.dil"; + byte[] dmeta_data = System.IO.File.ReadAllBytes(dmeta_name); + byte[] dil_data = System.IO.File.ReadAllBytes(dil_name); + byte[] dpdb_data = null; // TODO also use the dpdb data + + System.Reflection.Metadata.AssemblyExtensions.ApplyUpdate(assm, dmeta_data, dil_data, dpdb_data); + } + } +} diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/WebAssembly.Browser.HotReload.Test.csproj b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/WebAssembly.Browser.HotReload.Test.csproj new file mode 100644 index 0000000000000..2ba05527c2a42 --- /dev/null +++ b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/WebAssembly.Browser.HotReload.Test.csproj @@ -0,0 +1,52 @@ + + + true + false + false + true + WasmTestOnBrowser + 42 + runtime.js + false + + + + + + + Always + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/index.html b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/index.html new file mode 100644 index 0000000000000..ad7cc2164ba48 --- /dev/null +++ b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/index.html @@ -0,0 +1,55 @@ + + + + + + TESTS + + + + + + Result from Sample.Test.TestMeaning: + + + + + + + diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/runtime.js b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/runtime.js new file mode 100644 index 0000000000000..4859991a626f4 --- /dev/null +++ b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/runtime.js @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +var Module = { + + config: null, + + preInit: async function() { + Module.config = await MONO.mono_wasm_load_config("./mono-config.json"); + }, + + onRuntimeInitialized: function () { + if (!Module.config || Module.config.error) { + console.log("No config found"); + test_exit(1); + throw(Module.config.error); + } + + Module.config.loaded_cb = function () { + try { + App.init (); + } catch (error) { + test_exit(1); + throw (error); + } + }; + Module.config.fetch_file_cb = function (asset) { + return fetch (asset, { credentials: 'same-origin' }); + } + + if (Module.config.environment_variables !== undefined) { + console.log ("expected environment variables to be undefined, but they're: ", Module.config.environment_variables); + test_exit(1); + } + Module.config.environment_variables = { + "DOTNET_MODIFIABLE_ASSEMBLIES": "debug" + }; + + try + { + MONO.mono_load_runtime_and_bcl_args (Module.config); + } catch (error) { + test_exit(1); + throw(error); + } + }, +}; From f3af8d8b1468f55dea7b82c6bea3bbe0c956e72e Mon Sep 17 00:00:00 2001 From: Miha Zupan Date: Wed, 23 Jun 2021 17:21:42 -0700 Subject: [PATCH 091/107] Fix telemetry for Socket connects to Dns endpoints (#54071) --- .../Net/Sockets/SocketAsyncEventArgs.cs | 21 +-- .../System/Net/Sockets/SocketsTelemetry.cs | 2 +- .../tests/FunctionalTests/TelemetryTest.cs | 172 +++++++++--------- 3 files changed, 90 insertions(+), 105 deletions(-) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.cs index bda1217e01f20..699a447837e0d 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.cs @@ -74,7 +74,6 @@ public partial class SocketAsyncEventArgs : EventArgs, IDisposable private Socket? _currentSocket; private bool _userSocket; // if false when performing Connect, _currentSocket should be disposed private bool _disposeCalled; - private protected bool _disableTelemetry; // Controls thread safety via Interlocked. private const int Configuring = -1; @@ -202,7 +201,7 @@ public int BytesTransferred private void OnCompletedInternal() { - if (SocketsTelemetry.Log.IsEnabled() && !_disableTelemetry) AfterConnectAcceptTelemetry(); + if (SocketsTelemetry.Log.IsEnabled()) AfterConnectAcceptTelemetry(); OnCompleted(this); } @@ -813,11 +812,8 @@ caughtException is OperationCanceledException || } // Complete the operation. - if (SocketsTelemetry.Log.IsEnabled() && !_disableTelemetry) - { - LogBytesTransferEvents(_connectSocket?.SocketType, SocketAsyncOperation.Connect, internalArgs.BytesTransferred); - AfterConnectAcceptTelemetry(); - } + if (SocketsTelemetry.Log.IsEnabled()) LogBytesTransferEvents(_connectSocket?.SocketType, SocketAsyncOperation.Connect, internalArgs.BytesTransferred); + Complete(); // Clean up after our temporary arguments. @@ -842,12 +838,7 @@ private sealed class MultiConnectSocketAsyncEventArgs : SocketAsyncEventArgs, IV private ManualResetValueTaskSourceCore _mrvtsc; private int _isCompleted; - public MultiConnectSocketAsyncEventArgs() : base(unsafeSuppressExecutionContextFlow: false) - { - // Instances of this type are an implementation detail of an overarching connect operation. - // We don't want to emit telemetry specific to operations on this inner instance. - _disableTelemetry = true; - } + public MultiConnectSocketAsyncEventArgs() : base(unsafeSuppressExecutionContextFlow: false) { } public void GetResult(short token) => _mrvtsc.GetResult(token); public ValueTaskSourceStatus GetStatus(short token) => _mrvtsc.GetStatus(token); @@ -968,7 +959,7 @@ internal void FinishOperationSyncSuccess(int bytesTransferred, SocketFlags flags break; } - if (SocketsTelemetry.Log.IsEnabled() && !_disableTelemetry) LogBytesTransferEvents(_currentSocket?.SocketType, _completedOperation, bytesTransferred); + if (SocketsTelemetry.Log.IsEnabled()) LogBytesTransferEvents(_currentSocket?.SocketType, _completedOperation, bytesTransferred); Complete(); } @@ -1003,7 +994,7 @@ private void FinishOperationSync(SocketError socketError, int bytesTransferred, FinishOperationSyncFailure(socketError, bytesTransferred, flags); } - if (SocketsTelemetry.Log.IsEnabled() && !_disableTelemetry) AfterConnectAcceptTelemetry(); + if (SocketsTelemetry.Log.IsEnabled()) AfterConnectAcceptTelemetry(); } private static void LogBytesTransferEvents(SocketType? socketType, SocketAsyncOperation operation, int bytesTransferred) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketsTelemetry.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketsTelemetry.cs index 7b7641f2a4003..cb1e77f17aebc 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketsTelemetry.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketsTelemetry.cs @@ -113,7 +113,7 @@ public void AcceptStart(EndPoint address) { if (IsEnabled(EventLevel.Informational, EventKeywords.All)) { - AcceptStart(address.ToString()); + AcceptStart(address.Serialize().ToString()); } } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs index ad17579f90e6a..82bed83eabe0c 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs @@ -8,7 +8,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.DotNet.RemoteExecutor; -using Microsoft.DotNet.XUnitExtensions; using Xunit; using Xunit.Abstractions; @@ -117,14 +116,9 @@ await listener.RunWithCallbackAsync(e => events.Enqueue((e, e.ActivityId)), asyn await WaitForEventCountersAsync(events); }); - Assert.DoesNotContain(events, ev => ev.Event.EventId == 0); // errors from the EventSource itself - - VerifyStartStopEvents(events, connect: true, expectedCount: 1); - VerifyStartStopEvents(events, connect: false, expectedCount: 1); - - Assert.DoesNotContain(events, e => e.Event.EventName == "ConnectFailed"); - Assert.DoesNotContain(events, e => e.Event.EventName == "AcceptFailed"); + VerifyEvents(events, connect: true, expectedCount: 1); + VerifyEvents(events, connect: false, expectedCount: 1); VerifyEventCounters(events, connectCount: 1); }, connectMethod, acceptMethod).Dispose(); } @@ -153,12 +147,8 @@ await listener.RunWithCallbackAsync(e => events.Enqueue((e, e.ActivityId)), asyn await WaitForEventCountersAsync(events); }); - Assert.DoesNotContain(events, ev => ev.Event.EventId == 0); // errors from the EventSource itself - - VerifyStartStopEvents(events, connect: true, expectedCount: 1); - - Assert.DoesNotContain(events, e => e.Event.EventName == "ConnectFailed"); + VerifyEvents(events, connect: true, expectedCount: 1); VerifyEventCounters(events, connectCount: 1, connectOnly: true); }, connectMethod, useDnsEndPoint.ToString()).Dispose(); } @@ -169,12 +159,6 @@ await listener.RunWithCallbackAsync(e => events.Enqueue((e, e.ActivityId)), asyn [MemberData(nameof(SocketMethods_WithBools_MemberData))] public void EventSource_SocketConnectFailure_LogsConnectFailed(string connectMethod, bool useDnsEndPoint) { - if (useDnsEndPoint) - { - // [ActiveIssue("https://github.com/dotnet/runtime/issues/43931")] - throw new SkipTestException("https://github.com/dotnet/runtime/issues/43931"); - } - RemoteExecutor.Invoke(async (connectMethod, useDnsEndPointString) => { EndPoint endPoint = await GetRemoteEndPointAsync(useDnsEndPointString, port: 12345); @@ -207,7 +191,10 @@ await listener.RunWithCallbackAsync(e => events.Enqueue((e, e.ActivityId)), asyn await WaitForEventCountersAsync(events); }); - VerifyConnectFailureEvents(events); + // For DNS endpoints, we may see multiple Start/Failure/Stop events + int? expectedCount = bool.Parse(useDnsEndPointString) ? null : 1; + VerifyEvents(events, connect: true, expectedCount, shouldHaveFailures: true); + VerifyEventCounters(events, connectCount: 0); }, connectMethod, useDnsEndPoint.ToString()).Dispose(); } @@ -216,12 +203,6 @@ await listener.RunWithCallbackAsync(e => events.Enqueue((e, e.ActivityId)), asyn [MemberData(nameof(SocketMethods_MemberData))] public void EventSource_SocketAcceptFailure_LogsAcceptFailed(string acceptMethod) { - if (acceptMethod == "Sync" && PlatformDetection.IsRedHatFamily7) - { - // [ActiveIssue("https://github.com/dotnet/runtime/issues/42686")] - throw new SkipTestException("Disposing a Socket performing a sync operation can hang on RedHat7 systems"); - } - RemoteExecutor.Invoke(async acceptMethod => { using var listener = new TestEventListener("System.Net.Sockets", EventLevel.Verbose, 0.1); @@ -246,18 +227,8 @@ await Assert.ThrowsAnyAsync(async () => await WaitForEventCountersAsync(events); }); - Assert.DoesNotContain(events, ev => ev.Event.EventId == 0); // errors from the EventSource itself - - VerifyStartStopEvents(events, connect: false, expectedCount: 1); - - (EventWrittenEventArgs Event, Guid ActivityId) failed = Assert.Single(events, e => e.Event.EventName == "AcceptFailed"); - Assert.Equal(2, failed.Event.Payload.Count); - Assert.True(Enum.IsDefined((SocketError)failed.Event.Payload[0])); - Assert.IsType(failed.Event.Payload[1]); - - (_, Guid startActivityId) = Assert.Single(events, e => e.Event.EventName == "AcceptStart"); - Assert.Equal(startActivityId, failed.ActivityId); + VerifyEvents(events, connect: false, expectedCount: 1, shouldHaveFailures: true); VerifyEventCounters(events, connectCount: 0); }, acceptMethod).Dispose(); } @@ -270,12 +241,6 @@ await Assert.ThrowsAnyAsync(async () => [InlineData("Eap", false)] public void EventSource_ConnectAsyncCanceled_LogsConnectFailed(string connectMethod, bool useDnsEndPoint) { - if (useDnsEndPoint) - { - // [ActiveIssue("https://github.com/dotnet/runtime/issues/46030")] - throw new SkipTestException("https://github.com/dotnet/runtime/issues/46030"); - } - RemoteExecutor.Invoke(async (connectMethod, useDnsEndPointString) => { EndPoint endPoint = await GetRemoteEndPointAsync(useDnsEndPointString, port: 12345); @@ -326,27 +291,13 @@ await Assert.ThrowsAnyAsync(async () => await WaitForEventCountersAsync(events); }); - VerifyConnectFailureEvents(events); + // For DNS endpoints, we may see multiple Start/Failure/Stop events + int? expectedCount = bool.Parse(useDnsEndPointString) ? null : 1; + VerifyEvents(events, connect: true, expectedCount, shouldHaveFailures: true); + VerifyEventCounters(events, connectCount: 0); }, connectMethod, useDnsEndPoint.ToString()).Dispose(); } - private static void VerifyConnectFailureEvents(ConcurrentQueue<(EventWrittenEventArgs Event, Guid ActivityId)> events) - { - Assert.DoesNotContain(events, ev => ev.Event.EventId == 0); // errors from the EventSource itself - - VerifyStartStopEvents(events, connect: true, expectedCount: 1); - - (EventWrittenEventArgs Event, Guid ActivityId) failed = Assert.Single(events, e => e.Event.EventName == "ConnectFailed"); - Assert.Equal(2, failed.Event.Payload.Count); - Assert.True(Enum.IsDefined((SocketError)failed.Event.Payload[0])); - Assert.IsType(failed.Event.Payload[1]); - - (_, Guid startActivityId) = Assert.Single(events, e => e.Event.EventName == "ConnectStart"); - Assert.Equal(startActivityId, failed.ActivityId); - - VerifyEventCounters(events, connectCount: 0); - } - [OuterLoop] [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] public void EventSource_EventsRaisedAsExpected() @@ -382,40 +333,13 @@ await listener.RunWithCallbackAsync(e => events.Enqueue((e, e.ActivityId)), asyn await WaitForEventCountersAsync(events); }); - Assert.DoesNotContain(events, ev => ev.Event.EventId == 0); // errors from the EventSource itself - - VerifyStartStopEvents(events, connect: true, expectedCount: 10); - - Assert.DoesNotContain(events, e => e.Event.EventName == "ConnectFailed"); + VerifyEvents(events, connect: true, expectedCount: 10); VerifyEventCounters(events, connectCount: 10, shouldHaveTransferedBytes: true, shouldHaveDatagrams: true); } }).Dispose(); } - private static void VerifyStartStopEvents(ConcurrentQueue<(EventWrittenEventArgs Event, Guid ActivityId)> events, bool connect, int expectedCount) - { - string startName = connect ? "ConnectStart" : "AcceptStart"; - (EventWrittenEventArgs Event, Guid ActivityId)[] starts = events.Where(e => e.Event.EventName == startName).ToArray(); - Assert.Equal(expectedCount, starts.Length); - foreach ((EventWrittenEventArgs Event, _) in starts) - { - object startPayload = Assert.Single(Event.Payload); - Assert.False(string.IsNullOrWhiteSpace(startPayload as string)); - } - - string stopName = connect ? "ConnectStop" : "AcceptStop"; - (EventWrittenEventArgs Event, Guid ActivityId)[] stops = events.Where(e => e.Event.EventName == stopName).ToArray(); - Assert.Equal(expectedCount, stops.Length); - Assert.All(stops, stop => Assert.Empty(stop.Event.Payload)); - - for (int i = 0; i < expectedCount; i++) - { - Assert.NotEqual(Guid.Empty, starts[i].ActivityId); - Assert.Equal(starts[i].ActivityId, stops[i].ActivityId); - } - } - private static async Task WaitForEventAsync(ConcurrentQueue<(EventWrittenEventArgs Event, Guid ActivityId)> events, string name) { DateTime startTime = DateTime.UtcNow; @@ -452,6 +376,76 @@ static bool IsBytesSentEventCounter(EventWrittenEventArgs e) } } + private static void VerifyEvents(ConcurrentQueue<(EventWrittenEventArgs Event, Guid ActivityId)> events, bool connect, int? expectedCount, bool shouldHaveFailures = false) + { + bool start = false; + Guid startGuid = Guid.Empty; + bool seenFailures = false; + bool seenFailureAfterStart = false; + int numberOfStops = 0; + + foreach ((EventWrittenEventArgs Event, Guid ActivityId) in events) + { + Assert.False(Event.EventId == 0, $"Received an error event from EventSource: {Event.Message}"); + + if (Event.EventName.Contains("Connect") != connect) + { + continue; + } + + switch (Event.EventName) + { + case "ConnectStart": + case "AcceptStart": + Assert.False(start, "Start without a Stop"); + Assert.NotEqual(Guid.Empty, ActivityId); + startGuid = ActivityId; + seenFailureAfterStart = false; + start = true; + + string startAddress = Assert.IsType(Assert.Single(Event.Payload)); + Assert.Matches(@"^InterNetwork.*?:\d\d:{(?:\d{1,3},?)+}$", startAddress); + break; + + case "ConnectStop": + case "AcceptStop": + Assert.True(start, "Stop without a Start"); + Assert.Equal(startGuid, ActivityId); + startGuid = Guid.Empty; + numberOfStops++; + start = false; + + Assert.Empty(Event.Payload); + break; + + case "ConnectFailed": + case "AcceptFailed": + Assert.True(start, "Failed should come between Start and Stop"); + Assert.False(seenFailureAfterStart, "Start may only have one Failed event"); + Assert.Equal(startGuid, ActivityId); + seenFailureAfterStart = true; + seenFailures = true; + + Assert.Equal(2, Event.Payload.Count); + Assert.True(Enum.IsDefined((SocketError)Event.Payload[0])); + Assert.IsType(Event.Payload[1]); + break; + } + } + + Assert.False(start, "Start without a Stop"); + Assert.Equal(shouldHaveFailures, seenFailures); + + if (expectedCount.HasValue) + { + Assert.Equal(expectedCount, numberOfStops); + } + else + { + Assert.NotEqual(0, numberOfStops); + } + } + private static void VerifyEventCounters(ConcurrentQueue<(EventWrittenEventArgs Event, Guid ActivityId)> events, int connectCount, bool connectOnly = false, bool shouldHaveTransferedBytes = false, bool shouldHaveDatagrams = false) { Dictionary eventCounters = events From 4f3e30e6d343d5eb47e77e978c74c235d0bfcf4c Mon Sep 17 00:00:00 2001 From: Thays Grazia Date: Wed, 23 Jun 2021 22:49:43 -0300 Subject: [PATCH 092/107] [mono][wasm] Fix compilation error on wasm (#54659) --- src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index c296c8501473a..b3eebcd7e381d 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -1446,7 +1446,7 @@ public async Task StackFrameGetValues(SessionId sessionId, MethodInfo me { if (asyncLocal["name"].Value().EndsWith("__this")) asyncLocal["name"] = "this"; - else if (asyncLocal["name"].Value().Contains("<")) + else if (asyncLocal["name"].Value().Contains('<')) asyncLocal["name"] = Regex.Match(asyncLocal["name"].Value(), @"\<([^)]*)\>").Groups[1].Value; } return asyncLocals; From dae9156dd0d5b82c9f3fc98baeb5a75cc3f66211 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Wed, 23 Jun 2021 22:29:00 -0400 Subject: [PATCH 093/107] [wasm] Fix blazor/aot builds (#54651) `dotnet\packs\Microsoft.NET.Runtime.WebAssembly.Sdk\6.0.0-preview.7.21321.15\Sdk\WasmApp.Native.targets(342,5): error : Could not find AOT cross compiler at $(_MonoAotCrossCompilerPath)=` Make sure this is set for the aot path. --- src/mono/wasm/build/WasmApp.Native.targets | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/mono/wasm/build/WasmApp.Native.targets b/src/mono/wasm/build/WasmApp.Native.targets index c034763ca0f25..e1de26e4a4cde 100644 --- a/src/mono/wasm/build/WasmApp.Native.targets +++ b/src/mono/wasm/build/WasmApp.Native.targets @@ -359,6 +359,10 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_ --> + + <_MonoAotCrossCompilerPath>@(MonoAotCrossCompiler->WithMetadataValue('RuntimeIdentifier','browser-wasm')) + + From 914a42217a6370c33364265da305e1765f8aae33 Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Thu, 24 Jun 2021 05:52:40 +0200 Subject: [PATCH 094/107] [wasm] Enable fixed libraries tests (#54641) Fixed by https://github.com/dotnet/runtime/pull/52705 and https://github.com/dotnet/runtime/pull/52707 --- src/libraries/tests.proj | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index 5cad3c540880b..b5638f2a00daa 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -322,10 +322,8 @@ - - From 4facc363c445b51b63af8df2d278ce582546130e Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Thu, 24 Jun 2021 00:00:01 -0400 Subject: [PATCH 095/107] Remove Version.Clone from AssemblyName.Clone (#54621) Version is immutable. --- .../src/System/Reflection/AssemblyName.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs index 0853bfff0685a..4291dded86107 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs @@ -126,7 +126,7 @@ public object Clone() _publicKey = (byte[]?)_publicKey?.Clone(), _publicKeyToken = (byte[]?)_publicKeyToken?.Clone(), _cultureInfo = _cultureInfo, - _version = (Version?)_version?.Clone(), + _version = _version, _flags = _flags, _codeBase = _codeBase, _hashAlgorithm = _hashAlgorithm, From f55390a2e57cfe38fa692032195ed5a9f073e1d9 Mon Sep 17 00:00:00 2001 From: Dan Moseley Date: Wed, 23 Jun 2021 22:35:39 -0600 Subject: [PATCH 096/107] Fix sporadic double fd close (#54660) Fix https://github.com/dotnet/runtime/issues/54589 --- src/libraries/System.IO.FileSystem/tests/RandomAccess/Base.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.IO.FileSystem/tests/RandomAccess/Base.cs b/src/libraries/System.IO.FileSystem/tests/RandomAccess/Base.cs index 8d876f30174cd..45d0547a39191 100644 --- a/src/libraries/System.IO.FileSystem/tests/RandomAccess/Base.cs +++ b/src/libraries/System.IO.FileSystem/tests/RandomAccess/Base.cs @@ -46,7 +46,7 @@ public void ThrowsObjectDisposedExceptionForDisposedHandle() public void ThrowsNotSupportedExceptionForUnseekableFile() { using (var server = new AnonymousPipeServerStream(PipeDirection.Out)) - using (SafeFileHandle handle = new SafeFileHandle(server.SafePipeHandle.DangerousGetHandle(), true)) + using (SafeFileHandle handle = new SafeFileHandle(server.SafePipeHandle.DangerousGetHandle(), ownsHandle: false)) { Assert.Throws(() => MethodUnderTest(handle, Array.Empty(), 0)); } From 7833828914a42f8c99dfa6f18ccd47f99dc2b56e Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Thu, 24 Jun 2021 08:58:33 +0200 Subject: [PATCH 097/107] [FileStream] add tests for device and UNC paths (#54545) * add a test for unseekable device by using a path to named pipe * add a test for seekable device by using DeviceID instead of drive letter * add a test for a UNC file path (local file share) --- .../System/IO/FileCleanupTestBase.cs | 2 +- .../FileStreamConformanceTests.Windows.cs | 179 ++++++++++++++++++ ...stem.IO.FileSystem.Net5Compat.Tests.csproj | 1 + .../tests/System.IO.FileSystem.Tests.csproj | 2 + 4 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamConformanceTests.Windows.cs diff --git a/src/libraries/Common/tests/TestUtilities/System/IO/FileCleanupTestBase.cs b/src/libraries/Common/tests/TestUtilities/System/IO/FileCleanupTestBase.cs index a0542ef930c02..02ffa607c94aa 100644 --- a/src/libraries/Common/tests/TestUtilities/System/IO/FileCleanupTestBase.cs +++ b/src/libraries/Common/tests/TestUtilities/System/IO/FileCleanupTestBase.cs @@ -79,7 +79,7 @@ protected virtual void Dispose(bool disposing) /// An optional index value to use as a suffix on the file name. Typically a loop index. /// The member name of the function calling this method. /// The line number of the function calling this method. - protected string GetTestFilePath(int? index = null, [CallerMemberName] string memberName = null, [CallerLineNumber] int lineNumber = 0) => + protected virtual string GetTestFilePath(int? index = null, [CallerMemberName] string memberName = null, [CallerLineNumber] int lineNumber = 0) => Path.Combine(TestDirectory, GetTestFileName(index, memberName, lineNumber)); /// Gets a test file name that is associated with the call site. diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamConformanceTests.Windows.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamConformanceTests.Windows.cs new file mode 100644 index 0000000000000..1cfbd351824de --- /dev/null +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamConformanceTests.Windows.cs @@ -0,0 +1,179 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Win32.SafeHandles; +using System.ComponentModel; +using System.IO.Pipes; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; +using System.ServiceProcess; +using System.Threading.Tasks; +using Xunit; + +namespace System.IO.Tests +{ + [PlatformSpecific(TestPlatforms.Windows)] // DOS device paths (\\.\ and \\?\) are a Windows concept + public class UnseekableDeviceFileStreamConnectedConformanceTests : ConnectedStreamConformanceTests + { + protected override async Task CreateConnectedStreamsAsync() + { + string pipeName = FileSystemTest.GetNamedPipeServerStreamName(); + string pipePath = Path.GetFullPath($@"\\.\pipe\{pipeName}"); + + var server = new NamedPipeServerStream(pipeName, PipeDirection.In); + var clienStream = new FileStream(File.OpenHandle(pipePath, FileMode.Open, FileAccess.Write, FileShare.None), FileAccess.Write); + + await server.WaitForConnectionAsync(); + + var serverStrean = new FileStream(new SafeFileHandle(server.SafePipeHandle.DangerousGetHandle(), true), FileAccess.Read); + + server.SafePipeHandle.SetHandleAsInvalid(); + + return (serverStrean, clienStream); + } + + protected override Type UnsupportedConcurrentExceptionType => null; + protected override bool UsableAfterCanceledReads => false; + protected override bool FullyCancelableOperations => false; + protected override bool BlocksOnZeroByteReads => OperatingSystem.IsWindows(); + protected override bool SupportsConcurrentBidirectionalUse => false; + } + + [PlatformSpecific(TestPlatforms.Windows)] // DOS device paths (\\.\ and \\?\) are a Windows concept + public class SeekableDeviceFileStreamStandaloneConformanceTests : UnbufferedAsyncFileStreamStandaloneConformanceTests + { + protected override string GetTestFilePath(int? index = null, [CallerMemberName] string memberName = null, [CallerLineNumber] int lineNumber = 0) + { + string filePath = Path.GetFullPath(base.GetTestFilePath(index, memberName, lineNumber)); + string drive = Path.GetPathRoot(filePath); + StringBuilder volumeNameBuffer = new StringBuilder(filePath.Length + 1024); + + // the following method maps drive letter like "C:\" to a DeviceID (a DOS device path) + // example: "\\?\Volume{724edb31-eaa5-4728-a4e3-f2474fd34ae2}\" + if (!GetVolumeNameForVolumeMountPoint(drive, volumeNameBuffer, volumeNameBuffer.Capacity)) + { + throw new Win32Exception(Marshal.GetLastPInvokeError(), "GetVolumeNameForVolumeMountPoint failed"); + } + + // instead of: + // 'C:\Users\x\AppData\Local\Temp\y\z + // we want something like: + // '\\.\Volume{724edb31-eaa5-4728-a4e3-f2474fd34ae2}\Users\x\AppData\Local\Temp\y\z + string devicePath = filePath.Replace(drive, volumeNameBuffer.ToString()); + Assert.StartsWith(@"\\?\", devicePath); +#if DEBUG + // we do want to test \\.\ prefix as well + devicePath = devicePath.Replace(@"\\?\", @"\\.\"); +#endif + + return devicePath; + } + + [DllImport(Interop.Libraries.Kernel32, EntryPoint = "GetVolumeNameForVolumeMountPointW", CharSet = CharSet.Unicode, BestFitMapping = false, SetLastError = true)] + private static extern bool GetVolumeNameForVolumeMountPoint(string volumeName, StringBuilder uniqueVolumeName, int uniqueNameBufferCapacity); + } + + [PlatformSpecific(TestPlatforms.Windows)] // the test setup is Windows-specifc + [Collection("NoParallelTests")] // don't run in parallel, as file sharing logic is not thread-safe + [OuterLoop("Requires admin privileges to create a file share")] + [ConditionalClass(typeof(UncFilePathFileStreamStandaloneConformanceTests), nameof(CanShareFiles))] + public class UncFilePathFileStreamStandaloneConformanceTests : UnbufferedAsyncFileStreamStandaloneConformanceTests + { + public static bool CanShareFiles => _canShareFiles.Value; + + private static Lazy _canShareFiles = new Lazy(() => + { + if (!PlatformDetection.IsWindowsAndElevated || PlatformDetection.IsWindowsNanoServer) + { + return false; + } + + // the "Server Service" allows for file sharing. It can be disabled on some of our CI machines. + using (ServiceController sharingService = new ServiceController("Server")) + { + return sharingService.Status == ServiceControllerStatus.Running; + } + }); + + protected override string GetTestFilePath(int? index = null, [CallerMemberName] string memberName = null, [CallerLineNumber] int lineNumber = 0) + { + string testDirectoryPath = Path.GetFullPath(TestDirectory); + string shareName = new DirectoryInfo(testDirectoryPath).Name; + string fileName = GetTestFileName(index, memberName, lineNumber); + + SHARE_INFO_502 shareInfo = default; + shareInfo.shi502_netname = shareName; + shareInfo.shi502_path = testDirectoryPath; + shareInfo.shi502_remark = "folder created to test UNC file paths"; + shareInfo.shi502_max_uses = -1; + + int infoSize = Marshal.SizeOf(shareInfo); + IntPtr infoBuffer = Marshal.AllocCoTaskMem(infoSize); + + try + { + Marshal.StructureToPtr(shareInfo, infoBuffer, false); + + int shareResult = NetShareAdd(string.Empty, 502, infoBuffer, IntPtr.Zero); + + if (shareResult != 0 && shareResult != 2118) // is a failure that is not a NERR_DuplicateShare + { + throw new Exception($"Failed to create a file share, NetShareAdd returned {shareResult}"); + } + } + finally + { + Marshal.FreeCoTaskMem(infoBuffer); + } + + // now once the folder has been shared we can use "localhost" to access it: + // both type of slashes are valid, so let's test one for Debug and another for other configs +#if DEBUG + return @$"//localhost/{shareName}/{fileName}"; +#else + return @$"\\localhost\{shareName}\{fileName}"; +#endif + } + + protected override void Dispose(bool disposing) + { + string testDirectoryPath = Path.GetFullPath(TestDirectory); + string shareName = new DirectoryInfo(testDirectoryPath).Name; + + try + { + NetShareDel(string.Empty, shareName, 0); + } + finally + { + base.Dispose(disposing); + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct SHARE_INFO_502 + { + [MarshalAs(UnmanagedType.LPWStr)] + public string shi502_netname; + public uint shi502_type; + [MarshalAs(UnmanagedType.LPWStr)] + public string shi502_remark; + public int shi502_permissions; + public int shi502_max_uses; + public int shi502_current_uses; + [MarshalAs(UnmanagedType.LPWStr)] + public string shi502_path; + public IntPtr shi502_passwd; + public int shi502_reserved; + public IntPtr shi502_security_descriptor; + } + + [DllImport(Interop.Libraries.Netapi32)] + public static extern int NetShareAdd([MarshalAs(UnmanagedType.LPWStr)]string servername, int level, IntPtr buf, IntPtr parm_err); + + [DllImport(Interop.Libraries.Netapi32)] + public static extern int NetShareDel([MarshalAs(UnmanagedType.LPWStr)] string servername, [MarshalAs(UnmanagedType.LPWStr)] string netname, int reserved); + } +} diff --git a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj index 58669657e51b1..04d5762685710 100644 --- a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj +++ b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj @@ -25,6 +25,7 @@ + diff --git a/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj b/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj index 94cec86ce3a9a..bf845ce77b57f 100644 --- a/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj +++ b/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj @@ -72,6 +72,7 @@ + @@ -80,6 +81,7 @@ + From 72deae88e543b815339a7ba5a3d83a649e90b555 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Thu, 24 Jun 2021 12:38:53 +0200 Subject: [PATCH 098/107] get last error before calling a method that might fail as well (#54667) --- .../src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs index 7a13a80610d04..40c1e1866688c 100644 --- a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs @@ -44,8 +44,8 @@ private static SafeFileHandle Open(string path, Interop.Sys.OpenFlags flags, int if (handle.IsInvalid) { - handle.Dispose(); Interop.ErrorInfo error = Interop.Sys.GetLastErrorInfo(); + handle.Dispose(); // If we fail to open the file due to a path not existing, we need to know whether to blame // the file itself or its directory. If we're creating the file, then we blame the directory, @@ -70,8 +70,9 @@ private static SafeFileHandle Open(string path, Interop.Sys.OpenFlags flags, int Interop.Sys.FileStatus status; if (Interop.Sys.FStat(handle, out status) != 0) { + Interop.ErrorInfo error = Interop.Sys.GetLastErrorInfo(); handle.Dispose(); - throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), path); + throw Interop.GetExceptionForIoErrno(error, path); } if ((status.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFDIR) { From 1773f1666b548a17ea019fce8b62390c9713c089 Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Thu, 24 Jun 2021 14:06:06 +0200 Subject: [PATCH 099/107] exclude fragile tests (#54671) --- .../Common/tests/System/Net/Http/HttpClientHandlerTest.cs | 1 + .../System.Net.WebSockets.Client/tests/SendReceiveTest.cs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs index 33392864210a8..912f356c5d658 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs @@ -577,6 +577,7 @@ public static IEnumerable GetAsync_ManyDifferentResponseHeaders_Parsed [Theory] [MemberData(nameof(GetAsync_ManyDifferentResponseHeaders_ParsedCorrectly_MemberData))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54655", TestPlatforms.Browser)] public async Task GetAsync_ManyDifferentResponseHeaders_ParsedCorrectly(string newline, string fold, bool dribble) { if (LoopbackServerFactory.Version >= HttpVersion20.Value) diff --git a/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs b/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs index 6266f01e8ede5..bc70d2a4fdf55 100644 --- a/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs +++ b/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs @@ -217,6 +217,7 @@ public async Task SendAsync_MultipleOutstandingSendOperations_Throws(Uri server) [OuterLoop("Uses external servers", typeof(PlatformDetection), nameof(PlatformDetection.LocalEchoServerIsNotAvailable))] [ConditionalTheory(nameof(WebSocketsSupported)), MemberData(nameof(EchoServers))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/53957", TestPlatforms.Browser)] public async Task ReceiveAsync_MultipleOutstandingReceiveOperations_Throws(Uri server) { using (ClientWebSocket cws = await WebSocketHelper.GetConnectedWebSocket(server, TimeOutMilliseconds, _output)) @@ -320,6 +321,7 @@ await SendAsync( [OuterLoop("Uses external servers", typeof(PlatformDetection), nameof(PlatformDetection.LocalEchoServerIsNotAvailable))] [ConditionalTheory(nameof(WebSocketsSupported)), MemberData(nameof(EchoServers))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/53957", TestPlatforms.Browser)] public async Task SendReceive_VaryingLengthBuffers_Success(Uri server) { using (ClientWebSocket cws = await WebSocketHelper.GetConnectedWebSocket(server, TimeOutMilliseconds, _output)) From b317d0624f36caf7b7c6f61058e9f6663803f30c Mon Sep 17 00:00:00 2001 From: LateApexEarlySpeed <72254037+lateapexearlyspeed@users.noreply.github.com> Date: Thu, 24 Jun 2021 20:30:55 +0800 Subject: [PATCH 100/107] UdpClient with span support (#53429) Add API from #864 --- .../ref/System.Net.Sockets.cs | 7 + .../src/System/Net/Sockets/UDPClient.cs | 188 ++++++++++++++--- .../System/Net/Sockets/UdpReceiveResult.cs | 2 +- .../SendReceive/SendReceiveUdpClient.cs | 14 +- .../tests/FunctionalTests/TelemetryTest.cs | 4 +- .../tests/FunctionalTests/UdpClientTest.cs | 194 ++++++++++++++---- 6 files changed, 339 insertions(+), 70 deletions(-) diff --git a/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs b/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs index 0ea7f6b7ce72d..8ed4a71a4e805 100644 --- a/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs +++ b/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs @@ -760,12 +760,19 @@ public void JoinMulticastGroup(System.Net.IPAddress multicastAddr, int timeToLiv public void JoinMulticastGroup(System.Net.IPAddress multicastAddr, System.Net.IPAddress localAddress) { } public byte[] Receive([System.Diagnostics.CodeAnalysis.NotNullAttribute] ref System.Net.IPEndPoint? remoteEP) { throw null; } public System.Threading.Tasks.Task ReceiveAsync() { throw null; } + public System.Threading.Tasks.ValueTask ReceiveAsync(System.Threading.CancellationToken cancellationToken) { throw null; } public int Send(byte[] dgram, int bytes) { throw null; } + public int Send(System.ReadOnlySpan datagram) {throw null; } public int Send(byte[] dgram, int bytes, System.Net.IPEndPoint? endPoint) { throw null; } + public int Send(System.ReadOnlySpan datagram, System.Net.IPEndPoint? endPoint) { throw null; } public int Send(byte[] dgram, int bytes, string? hostname, int port) { throw null; } + public int Send(System.ReadOnlySpan datagram, string? hostname, int port) { throw null; } public System.Threading.Tasks.Task SendAsync(byte[] datagram, int bytes) { throw null; } + public System.Threading.Tasks.ValueTask SendAsync(System.ReadOnlyMemory datagram, System.Threading.CancellationToken cancellationToken = default) { throw null; } public System.Threading.Tasks.Task SendAsync(byte[] datagram, int bytes, System.Net.IPEndPoint? endPoint) { throw null; } + public System.Threading.Tasks.ValueTask SendAsync(System.ReadOnlyMemory datagram, System.Net.IPEndPoint? endPoint, System.Threading.CancellationToken cancellationToken = default) { throw null; } public System.Threading.Tasks.Task SendAsync(byte[] datagram, int bytes, string? hostname, int port) { throw null; } + public System.Threading.Tasks.ValueTask SendAsync(System.ReadOnlyMemory datagram, string? hostname, int port, System.Threading.CancellationToken cancellationToken = default) { throw null; } } public partial struct UdpReceiveResult : System.IEquatable { diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UDPClient.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UDPClient.cs index e27a77da1f81d..246483d0815fb 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UDPClient.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UDPClient.cs @@ -4,6 +4,7 @@ using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; using System.Runtime.Versioning; +using System.Threading; namespace System.Net.Sockets { @@ -600,9 +601,46 @@ public void DropMulticastGroup(IPAddress multicastAddr, int ifindex) public Task SendAsync(byte[] datagram, int bytes) => SendAsync(datagram, bytes, null); + /// + /// Sends a UDP datagram asynchronously to a remote host. + /// + /// + /// An of Type that specifies the UDP datagram that you intend to send. + /// + /// + /// The token to monitor for cancellation requests. The default value is None. + /// + /// A that represents the asynchronous send operation. The value of its Result property contains the number of bytes sent. + /// The is closed. + /// An error occurred when accessing the socket. + public ValueTask SendAsync(ReadOnlyMemory datagram, CancellationToken cancellationToken = default) => + SendAsync(datagram, null, cancellationToken); + public Task SendAsync(byte[] datagram, int bytes, string? hostname, int port) => SendAsync(datagram, bytes, GetEndpoint(hostname, port)); + /// + /// Sends a UDP datagram asynchronously to a remote host. + /// + /// + /// An of Type that specifies the UDP datagram that you intend to send. + /// + /// + /// The name of the remote host to which you intend to send the datagram. + /// + /// + /// The remote port number with which you intend to communicate. + /// + /// + /// The token to monitor for cancellation requests. The default value is None. + /// + /// A that represents the asynchronous send operation. The value of its Result property contains the number of bytes sent. + /// The has already established a default remote host. + /// The is closed. + /// An error occurred when accessing the socket. + public ValueTask SendAsync(ReadOnlyMemory datagram, string? hostname, int port, CancellationToken cancellationToken = default) => + SendAsync(datagram, GetEndpoint(hostname, port), cancellationToken); + public Task SendAsync(byte[] datagram, int bytes, IPEndPoint? endPoint) { ValidateDatagram(datagram, bytes, endPoint); @@ -618,6 +656,39 @@ public Task SendAsync(byte[] datagram, int bytes, IPEndPoint? endPoint) } } + /// + /// Sends a UDP datagram asynchronously to a remote host. + /// + /// + /// An of Type that specifies the UDP datagram that you intend to send. + /// + /// + /// An that represents the host and port to which to send the datagram. + /// + /// + /// The token to monitor for cancellation requests. The default value is None. + /// + /// A that represents the asynchronous send operation. The value of its Result property contains the number of bytes sent. + /// has already established a default remote host and is not . + /// The is closed. + /// An error occurred when accessing the socket. + public ValueTask SendAsync(ReadOnlyMemory datagram, IPEndPoint? endPoint, CancellationToken cancellationToken = default) + { + ThrowIfDisposed(); + + if (endPoint is null) + { + return _clientSocket.SendAsync(datagram, SocketFlags.None, cancellationToken); + } + if (_active) + { + // Do not allow sending packets to arbitrary host when connected. + throw new InvalidOperationException(SR.net_udpconnected); + } + CheckForBroadcast(endPoint.Address); + return _clientSocket.SendToAsync(datagram, SocketFlags.None, endPoint, cancellationToken); + } + public Task ReceiveAsync() { ThrowIfDisposed(); @@ -639,6 +710,36 @@ async Task WaitAndWrap(Task task) } } + /// + /// Returns a UDP datagram asynchronously that was sent by a remote host. + /// + /// + /// The token to monitor for cancellation requests. + /// + /// A representing the asynchronous operation. + /// The underlying has been closed. + /// An error occurred when accessing the socket. + public ValueTask ReceiveAsync(CancellationToken cancellationToken) + { + ThrowIfDisposed(); + + return WaitAndWrap(_clientSocket.ReceiveFromAsync( + _buffer, + SocketFlags.None, + _family == AddressFamily.InterNetwork ? IPEndPointStatics.Any : IPEndPointStatics.IPv6Any, cancellationToken)); + + async ValueTask WaitAndWrap(ValueTask task) + { + SocketReceiveFromResult result = await task.ConfigureAwait(false); + + byte[] buffer = result.ReceivedBytes < MaxUDPSize ? + _buffer.AsSpan(0, result.ReceivedBytes).ToArray() : + _buffer; + + return new UdpReceiveResult(buffer, (IPEndPoint)result.RemoteEndPoint); + } + } + private void CreateClientSocket() { // Common initialization code. @@ -892,45 +993,59 @@ public int Send(byte[] dgram, int bytes, IPEndPoint? endPoint) return Client.SendTo(dgram, 0, bytes, SocketFlags.None, endPoint); } - - // Sends a UDP datagram to the specified port on the specified remote host. - public int Send(byte[] dgram, int bytes, string? hostname, int port) + /// + /// Sends a UDP datagram to the host at the specified remote endpoint. + /// + /// + /// An of Type that specifies the UDP datagram that you intend to send. + /// + /// + /// An that represents the host and port to which to send the datagram. + /// + /// The number of bytes sent. + /// has already established a default remote host and is not . + /// is closed. + /// An error occurred when accessing the socket. + public int Send(ReadOnlySpan datagram, IPEndPoint? endPoint) { ThrowIfDisposed(); - if (dgram == null) - { - throw new ArgumentNullException(nameof(dgram)); - } - if (_active && ((hostname != null) || (port != 0))) + if (_active && endPoint != null) { // Do not allow sending packets to arbitrary host when connected throw new InvalidOperationException(SR.net_udpconnected); } - if (hostname == null || port == 0) - { - return Client.Send(dgram, 0, bytes, SocketFlags.None); - } - - IPAddress[] addresses = Dns.GetHostAddresses(hostname); - - int i = 0; - for (; i < addresses.Length && !IsAddressFamilyCompatible(addresses[i].AddressFamily); i++) + if (endPoint == null) { - ; // just count the addresses + return Client.Send(datagram, SocketFlags.None); } - if (addresses.Length == 0 || i == addresses.Length) - { - throw new ArgumentException(SR.net_invalidAddressList, nameof(hostname)); - } + CheckForBroadcast(endPoint.Address); - CheckForBroadcast(addresses[i]); - IPEndPoint ipEndPoint = new IPEndPoint(addresses[i], port); - return Client.SendTo(dgram, 0, bytes, SocketFlags.None, ipEndPoint); + return Client.SendTo(datagram, SocketFlags.None, endPoint); } + // Sends a UDP datagram to the specified port on the specified remote host. + public int Send(byte[] dgram, int bytes, string? hostname, int port) => Send(dgram, bytes, GetEndpoint(hostname, port)); + + /// + /// Sends a UDP datagram to a specified port on a specified remote host. + /// + /// + /// An of Type that specifies the UDP datagram that you intend to send. + /// + /// + /// The name of the remote host to which you intend to send the datagram. + /// + /// + /// The remote port number with which you intend to communicate. + /// + /// The number of bytes sent. + /// The has already established a default remote host. + /// The is closed. + /// An error occurred when accessing the socket. + public int Send(ReadOnlySpan datagram, string? hostname, int port) => Send(datagram, GetEndpoint(hostname, port)); // Sends a UDP datagram to a remote host. public int Send(byte[] dgram, int bytes) @@ -950,6 +1065,29 @@ public int Send(byte[] dgram, int bytes) return Client.Send(dgram, 0, bytes, SocketFlags.None); } + /// + /// Sends a UDP datagram to a remote host. + /// + /// + /// An of Type that specifies the UDP datagram that you intend to send. + /// + /// The number of bytes sent. + /// The has not established a default remote host. + /// The is closed. + /// An error occurred when accessing the socket. + public int Send(ReadOnlySpan datagram) + { + ThrowIfDisposed(); + + if (!_active) + { + // only allowed on connected socket + throw new InvalidOperationException(SR.net_notconnected); + } + + return Client.Send(datagram, SocketFlags.None); + } + private void ThrowIfDisposed() { if (_disposed) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UdpReceiveResult.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UdpReceiveResult.cs index 25af4a45bbaa1..55535f0583009 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UdpReceiveResult.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UdpReceiveResult.cs @@ -6,7 +6,7 @@ namespace System.Net.Sockets { /// - /// Presents UDP receive result information from a call to the method + /// Presents UDP receive result information from a call to the and method /// public struct UdpReceiveResult : IEquatable { diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveUdpClient.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveUdpClient.cs index ce83af9bb677d..252b862bce1ab 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveUdpClient.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveUdpClient.cs @@ -11,8 +11,8 @@ public sealed class SendReceiveUdpClient : MemberDatas { [OuterLoop] [Theory] - [MemberData(nameof(Loopbacks))] - public async Task SendToRecvFromAsync_Datagram_UDP_UdpClient(IPAddress loopbackAddress) + [MemberData(nameof(LoopbacksAndUseMemory))] + public async Task SendToRecvFromAsync_Datagram_UDP_UdpClient(IPAddress loopbackAddress, bool useMemoryOverload) { IPAddress leftAddress = loopbackAddress, rightAddress = loopbackAddress; @@ -66,7 +66,7 @@ public async Task SendToRecvFromAsync_Datagram_UDP_UdpClient(IPAddress loopbackA random.NextBytes(sendBuffer); sendBuffer[0] = (byte)sentDatagrams; - int sent = await right.SendAsync(sendBuffer, DatagramSize, leftEndpoint); + int sent = useMemoryOverload ? await right.SendAsync(new ReadOnlyMemory(sendBuffer), leftEndpoint) : await right.SendAsync(sendBuffer, DatagramSize, leftEndpoint); Assert.True(receiverAck.Wait(AckTimeout)); receiverAck.Reset(); @@ -85,5 +85,13 @@ public async Task SendToRecvFromAsync_Datagram_UDP_UdpClient(IPAddress loopbackA } } } + + public static readonly object[][] LoopbacksAndUseMemory = new object[][] + { + new object[] { IPAddress.IPv6Loopback, true }, + new object[] { IPAddress.IPv6Loopback, false }, + new object[] { IPAddress.Loopback, true }, + new object[] { IPAddress.Loopback, false }, + }; } } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs index 82bed83eabe0c..7b507a18f4037 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs @@ -325,8 +325,8 @@ await listener.RunWithCallbackAsync(e => events.Enqueue((e, e.ActivityId)), asyn await new SendReceive_Apm(null).SendRecv_Stream_TCP(IPAddress.Loopback, false).ConfigureAwait(false); await new SendReceive_Apm(null).SendRecv_Stream_TCP(IPAddress.Loopback, true).ConfigureAwait(false); - await new SendReceiveUdpClient().SendToRecvFromAsync_Datagram_UDP_UdpClient(IPAddress.Loopback).ConfigureAwait(false); - await new SendReceiveUdpClient().SendToRecvFromAsync_Datagram_UDP_UdpClient(IPAddress.Loopback).ConfigureAwait(false); + await new SendReceiveUdpClient().SendToRecvFromAsync_Datagram_UDP_UdpClient(IPAddress.Loopback, false).ConfigureAwait(false); + await new SendReceiveUdpClient().SendToRecvFromAsync_Datagram_UDP_UdpClient(IPAddress.Loopback, false).ConfigureAwait(false); await new NetworkStreamTest().CopyToAsync_AllDataCopied(4096, true).ConfigureAwait(false); await new NetworkStreamTest().Timeout_Roundtrips().ConfigureAwait(false); diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs index 3b4479629507b..66f69e1ce70da 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs @@ -59,7 +59,6 @@ public void Ctor_NullEndpoint_Throws() AssertExtensions.Throws("localEP", () => new UdpClient(null)); } - [OuterLoop] [Fact] public void Ctor_CanSend() { @@ -70,7 +69,6 @@ public void Ctor_CanSend() } } - [OuterLoop] [Fact] public void Ctor_Int_CanSend() { @@ -88,7 +86,6 @@ public void Ctor_Int_CanSend() } } - [OuterLoop] [Fact] public void Ctor_IntAddressFamily_IPv4_CanSend() { @@ -106,7 +103,6 @@ public void Ctor_IntAddressFamily_IPv4_CanSend() } } - [OuterLoop] [Fact] public void Ctor_IntAddressFamily_IPv6_CanSend() { @@ -124,7 +120,6 @@ public void Ctor_IntAddressFamily_IPv6_CanSend() } } - [OuterLoop] [Fact] public void Ctor_IPEndPoint_CanSend() { @@ -142,7 +137,6 @@ public void Ctor_IPEndPoint_CanSend() } } - [OuterLoop] [Fact] public void Ctor_StringInt_CanSend() { @@ -191,6 +185,21 @@ public void DisposeClose_OperationsThrow(bool close) Assert.Throws(() => udpClient.Send(null, 0, remoteEP)); Assert.Throws(() => udpClient.Send(null, 0)); Assert.Throws(() => udpClient.Send(null, 0, "localhost", 0)); + + Assert.Throws(() => udpClient.Send(new ReadOnlySpan(), remoteEP)); + Assert.Throws(() => udpClient.Send(new ReadOnlySpan())); + Assert.Throws(() => udpClient.Send(new ReadOnlySpan(), "localhost", 0)); + + Assert.Throws(() => {udpClient.SendAsync(null, 0, remoteEP);}); + Assert.Throws(() => {udpClient.SendAsync(null, 0);}); + Assert.Throws(() => {udpClient.SendAsync(null, 0, "localhost", 0);}); + + Assert.Throws(() => udpClient.SendAsync(new ReadOnlyMemory(), remoteEP)); + Assert.Throws(() => udpClient.SendAsync(new ReadOnlyMemory())); + Assert.Throws(() => udpClient.SendAsync(new ReadOnlyMemory(), "localhost", 0)); + + Assert.Throws(() => {udpClient.ReceiveAsync();}); + Assert.Throws(() => udpClient.ReceiveAsync(default)); } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsPreciseGcSupported))] @@ -321,7 +330,6 @@ public void InvalidArguments_Throw() } } - [OuterLoop] [Fact] public void BeginSend_NegativeBytes_Throws() { @@ -337,7 +345,6 @@ public void BeginSend_NegativeBytes_Throws() } } - [OuterLoop] [Fact] public void BeginSend_BytesMoreThanArrayLength_Throws() { @@ -353,7 +360,6 @@ public void BeginSend_BytesMoreThanArrayLength_Throws() } } - [OuterLoop] [Fact] public void BeginSend_AsyncOperationCompletes_Success() { @@ -377,8 +383,12 @@ public void Send_InvalidArguments_Throws() AssertExtensions.Throws("dgram", () => udpClient.Send(null, 0, "localhost", 0)); AssertExtensions.Throws("dgram", () => udpClient.Send(null, 0, new IPEndPoint(IPAddress.Loopback, 0))); Assert.Throws(() => udpClient.Send(new byte[1], 1)); + Assert.Throws(() => udpClient.Send(new ReadOnlySpan(new byte[1]))); udpClient.Active = true; Assert.Throws(() => udpClient.Send(new byte[1], 1, new IPEndPoint(IPAddress.Loopback, 0))); + Assert.Throws(() => udpClient.Send(new ReadOnlySpan(new byte[1]), new IPEndPoint(IPAddress.Loopback, 0))); + Assert.Throws(() => {udpClient.SendAsync(new byte[1], 1, new IPEndPoint(IPAddress.Loopback, 0));}); + Assert.Throws(() => udpClient.SendAsync(new ReadOnlyMemory(new byte[1]), new IPEndPoint(IPAddress.Loopback, 0))); } } @@ -389,10 +399,17 @@ public void Send_InvalidArguments_StringInt_Throws() using (var udpClient = new UdpClient("localhost", 0)) { Assert.Throws(() => udpClient.Send(new byte[1], 1, "localhost", 0)); + Assert.Throws(() => udpClient.Send(new ReadOnlySpan(new byte[1]), "localhost", 0)); + Assert.Throws(() => {udpClient.SendAsync(new byte[1], 1, "localhost", 0);}); + Assert.Throws(() => udpClient.SendAsync(new ReadOnlyMemory(new byte[1]), "localhost", 0)); + + Assert.Throws(() => udpClient.Send(new byte[1], 1, null, UnusedPort)); + Assert.Throws(() => udpClient.Send(new ReadOnlySpan(new byte[1]), null, UnusedPort)); + Assert.Throws(() => {udpClient.SendAsync(new byte[1], 1, null, UnusedPort);}); + Assert.Throws(() => udpClient.SendAsync(new ReadOnlyMemory(new byte[1]), null, UnusedPort)); } } - [OuterLoop] [Fact] public void Client_Idempotent() { @@ -421,7 +438,6 @@ public void Connect_InvalidArguments_Throws() } } - [OuterLoop] [Fact] public async Task ConnectAsync_StringHost_Success() { @@ -431,7 +447,6 @@ public async Task ConnectAsync_StringHost_Success() } } - [OuterLoop] [Fact] public async Task ConnectAsync_IPAddressHost_Success() { @@ -441,7 +456,6 @@ public async Task ConnectAsync_IPAddressHost_Success() } } - [OuterLoop] [Fact] public void Connect_StringHost_Success() { @@ -451,7 +465,6 @@ public void Connect_StringHost_Success() } } - [OuterLoop] [Fact] public void Connect_IPAddressHost_Success() { @@ -468,7 +481,6 @@ private void AsyncCompleted(IAsyncResult ar) _waitHandle.Set(); } - [OuterLoop] [Theory] [PlatformSpecific(TestPlatforms.Windows)] // Udp.AllowNatTraversal only supported on Windows [InlineData(true, IPProtectionLevel.Unrestricted)] @@ -482,7 +494,6 @@ public void AllowNatTraversal_Windows(bool allow, IPProtectionLevel resultLevel) } } - [OuterLoop] [Theory] [PlatformSpecific(TestPlatforms.AnyUnix)] // Udp.AllowNatTraversal throws PNSE on Unix [InlineData(true)] @@ -495,7 +506,6 @@ public void AllowNatTraversal_AnyUnix(bool allow) } } - [OuterLoop] [Theory] [InlineData(false)] [InlineData(true)] @@ -507,32 +517,55 @@ public void Send_Receive_Success(bool ipv4) using (var sender = new UdpClient(new IPEndPoint(address, 0))) { sender.Send(new byte[1], 1, new IPEndPoint(address, ((IPEndPoint)receiver.Client.LocalEndPoint).Port)); + AssertReceive(receiver); - IPEndPoint remoteEP = null; - byte[] data = receiver.Receive(ref remoteEP); - Assert.NotNull(remoteEP); - Assert.InRange(data.Length, 1, int.MaxValue); + sender.Send(new ReadOnlySpan(new byte[1]), new IPEndPoint(address, ((IPEndPoint)receiver.Client.LocalEndPoint).Port)); + AssertReceive(receiver); + } + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + [PlatformSpecific(TestPlatforms.Windows)] // "localhost" resolves to IPv4 & IPV6 on Windows, but may resolve to only one of those on Unix + public void Send_Receive_With_HostName_Success(bool ipv4) + { + IPAddress address = ipv4 ? IPAddress.Loopback : IPAddress.IPv6Loopback; + + using (var receiver = new UdpClient(new IPEndPoint(address, 0))) + using (var sender = new UdpClient(new IPEndPoint(address, 0))) + { + sender.Send(new byte[1], 1, "localhost", ((IPEndPoint)receiver.Client.LocalEndPoint).Port); + AssertReceive(receiver); + + sender.Send(new ReadOnlySpan(new byte[1]), "localhost", ((IPEndPoint)receiver.Client.LocalEndPoint).Port); + AssertReceive(receiver); } } [Fact] [PlatformSpecific(TestPlatforms.Windows)] // "localhost" resolves to IPv4 & IPV6 on Windows, but may resolve to only one of those on Unix - [OuterLoop] public void Send_Receive_Connected_Success() { using (var receiver = new UdpClient("localhost", 0)) using (var sender = new UdpClient("localhost", ((IPEndPoint)receiver.Client.LocalEndPoint).Port)) { sender.Send(new byte[1], 1); + AssertReceive(receiver); - IPEndPoint remoteEP = null; - byte[] data = receiver.Receive(ref remoteEP); - Assert.NotNull(remoteEP); - Assert.InRange(data.Length, 1, int.MaxValue); + sender.Send(new ReadOnlySpan(new byte[1])); + AssertReceive(receiver); } } - [OuterLoop] + private static void AssertReceive(UdpClient receiver) + { + IPEndPoint remoteEP = null; + byte[] data = receiver.Receive(ref remoteEP); + Assert.NotNull(remoteEP); + Assert.InRange(data.Length, 1, int.MaxValue); + } + [Theory] [InlineData(false)] [InlineData(true)] @@ -549,7 +582,6 @@ public void Send_Available_Success(bool ipv4) } } - [OuterLoop] [Theory] [InlineData(false)] [InlineData(true)] @@ -571,7 +603,6 @@ public void BeginEndSend_BeginEndReceive_Success(bool ipv4) [Fact] [PlatformSpecific(TestPlatforms.Windows)] // "localhost" resolves to IPv4 & IPV6 on Windows, but may resolve to only one of those on Unix - [OuterLoop] public void BeginEndSend_BeginEndReceive_Connected_Success() { using (var receiver = new UdpClient("localhost", 0)) @@ -586,7 +617,6 @@ public void BeginEndSend_BeginEndReceive_Connected_Success() } } - [OuterLoop] [Theory] [InlineData(false)] [InlineData(true)] @@ -598,28 +628,114 @@ public async Task SendAsync_ReceiveAsync_Success(bool ipv4) using (var sender = new UdpClient(new IPEndPoint(address, 0))) { await sender.SendAsync(new byte[1], 1, new IPEndPoint(address, ((IPEndPoint)receiver.Client.LocalEndPoint).Port)); + await AssertReceiveAsync(receiver); + + await sender.SendAsync(new ReadOnlyMemory(new byte[1]), new IPEndPoint(address, ((IPEndPoint)receiver.Client.LocalEndPoint).Port)); + await AssertReceiveAsync(receiver); + } + } - UdpReceiveResult result = await receiver.ReceiveAsync(); - Assert.NotNull(result.RemoteEndPoint); - Assert.NotNull(result.Buffer); - Assert.InRange(result.Buffer.Length, 1, int.MaxValue); + [Theory] + [InlineData(false)] + [InlineData(true)] + [PlatformSpecific(TestPlatforms.Windows)] // "localhost" resolves to IPv4 & IPV6 on Windows, but may resolve to only one of those on Unix + public async Task SendAsync_ReceiveAsync_With_HostName_Success(bool ipv4) + { + IPAddress address = ipv4 ? IPAddress.Loopback : IPAddress.IPv6Loopback; + + using (var receiver = new UdpClient(new IPEndPoint(address, 0))) + using (var sender = new UdpClient(new IPEndPoint(address, 0))) + { + await sender.SendAsync(new byte[1], "localhost", ((IPEndPoint)receiver.Client.LocalEndPoint).Port); + await AssertReceiveAsync(receiver); + + await sender.SendAsync(new ReadOnlyMemory(new byte[1]), "localhost", ((IPEndPoint)receiver.Client.LocalEndPoint).Port); + await AssertReceiveAsync(receiver); + } + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task ReceiveAsync_Cancel_Throw(bool ipv4) + { + IPAddress address = ipv4 ? IPAddress.Loopback : IPAddress.IPv6Loopback; + + using (var receiver = new UdpClient(new IPEndPoint(address, 0))) + { + using (var timeoutCts = new CancellationTokenSource(1)) + { + await Assert.ThrowsAnyAsync(() => receiver.ReceiveAsync(timeoutCts.Token).AsTask()); + } } } [Fact] [PlatformSpecific(TestPlatforms.Windows)] // "localhost" resolves to IPv4 & IPV6 on Windows, but may resolve to only one of those on Unix - [OuterLoop] public async Task SendAsync_ReceiveAsync_Connected_Success() { using (var receiver = new UdpClient("localhost", 0)) using (var sender = new UdpClient("localhost", ((IPEndPoint)receiver.Client.LocalEndPoint).Port)) { await sender.SendAsync(new byte[1], 1); + await AssertReceiveAsync(receiver); + + await sender.SendAsync(new ReadOnlyMemory(new byte[1])); + await AssertReceiveAsync(receiver); + + await sender.SendAsync(new ReadOnlyMemory(new byte[1]), null); + await AssertReceiveAsync(receiver); + + await sender.SendAsync(new ReadOnlyMemory(new byte[1]), null, 0); + await AssertReceiveAsync(receiver); + } + } + + private static async Task AssertReceiveAsync(UdpClient receiver) + { + UdpReceiveResult result = await receiver.ReceiveAsync(); + Assert.NotNull(result.RemoteEndPoint); + Assert.NotNull(result.Buffer); + Assert.InRange(result.Buffer.Length, 1, int.MaxValue); + } + + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] // "localhost" resolves to IPv4 & IPV6 on Windows, but may resolve to only one of those on Unix + public async Task SendAsync_Connected_PreCanceled_Throws() + { + using (var receiver = new UdpClient("localhost", 0)) + using (var sender = new UdpClient("localhost", ((IPEndPoint)receiver.Client.LocalEndPoint).Port)) + { + await Assert.ThrowsAnyAsync(() => sender.SendAsync(new ReadOnlyMemory(new byte[1]), new CancellationToken(true)).AsTask()); + } + } - UdpReceiveResult result = await receiver.ReceiveAsync(); - Assert.NotNull(result.RemoteEndPoint); - Assert.NotNull(result.Buffer); - Assert.InRange(result.Buffer.Length, 1, int.MaxValue); + [Theory] + [InlineData(false)] + [InlineData(true)] + [PlatformSpecific(TestPlatforms.Windows)] // "localhost" resolves to IPv4 & IPV6 on Windows, but may resolve to only one of those on Unix + public async Task SendAsync_With_HostName_PreCanceled_Throws(bool ipv4) + { + IPAddress address = ipv4 ? IPAddress.Loopback : IPAddress.IPv6Loopback; + + using (var receiver = new UdpClient(new IPEndPoint(address, 0))) + using (var sender = new UdpClient(new IPEndPoint(address, 0))) + { + await Assert.ThrowsAnyAsync(() => sender.SendAsync(new ReadOnlyMemory(new byte[1]), "localhost", ((IPEndPoint)receiver.Client.LocalEndPoint).Port, new CancellationToken(true)).AsTask()); + } + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task SendAsync_PreCanceled_Throws(bool ipv4) + { + IPAddress address = ipv4 ? IPAddress.Loopback : IPAddress.IPv6Loopback; + + using (var receiver = new UdpClient(new IPEndPoint(address, 0))) + using (var sender = new UdpClient(new IPEndPoint(address, 0))) + { + await Assert.ThrowsAnyAsync(() => sender.SendAsync(new ReadOnlyMemory(new byte[1]), new IPEndPoint(address, ((IPEndPoint)receiver.Client.LocalEndPoint).Port), new CancellationToken(true)).AsTask()); } } From 5c2ff179be30c6618baa6473feda19fcaebe5116 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Thu, 24 Jun 2021 10:24:47 -0400 Subject: [PATCH 101/107] Add PeriodicTimer (#53899) --- .../System.Private.CoreLib.Shared.projitems | 1 + .../src/System/Threading/PeriodicTimer.cs | 220 ++++++++++++++++++ .../System.Runtime/ref/System.Runtime.cs | 6 + .../tests/System.Runtime.Tests.csproj | 1 + .../System/Threading/PeriodicTimerTests.cs | 203 ++++++++++++++++ 5 files changed, 431 insertions(+) create mode 100644 src/libraries/System.Private.CoreLib/src/System/Threading/PeriodicTimer.cs create mode 100644 src/libraries/System.Runtime/tests/System/Threading/PeriodicTimerTests.cs 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 2af41ff13424b..1186b2c606f63 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -1069,6 +1069,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PeriodicTimer.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PeriodicTimer.cs new file mode 100644 index 0000000000000..b16c4d067b7ed --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PeriodicTimer.cs @@ -0,0 +1,220 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.ExceptionServices; +using System.Threading.Tasks; +using System.Threading.Tasks.Sources; + +namespace System.Threading +{ + /// Provides a periodic timer that enables waiting asynchronously for timer ticks. + /// + /// This timer is intended to be used only by a single consumer at a time: only one call to + /// may be in flight at any given moment. may be used concurrently with an active + /// to interrupt it and cause it to return false. + /// + public sealed class PeriodicTimer : IDisposable + { + /// The underlying timer. + private readonly TimerQueueTimer _timer; + /// All state other than the _timer, so that the rooted timer's callback doesn't indirectly root itself by referring to _timer. + private readonly State _state; + + /// Initializes the timer. + /// The time interval between invocations of callback.. + /// must be represent a number of milliseconds larger than 0 and smaller than . + public PeriodicTimer(TimeSpan period) + { + long ms = (long)period.TotalMilliseconds; + if (ms < 1 || ms > Timer.MaxSupportedTimeout) + { + GC.SuppressFinalize(this); + throw new ArgumentOutOfRangeException(nameof(period)); + } + + _state = new State(); + _timer = new TimerQueueTimer(s => ((State)s!).Signal(), _state, (uint)ms, (uint)ms, flowExecutionContext: false); + } + + /// Wait for the next tick of the timer, or for the timer to be stopped. + /// + /// A to use to cancel the asynchronous wait. If cancellation is requested, it affects only the single wait operation; + /// the underlying timer continues firing. + /// + /// A task that will be completed due to the timer firing, being called to stop the timer, or cancellation being requested. + /// + /// The behaves like an auto-reset event, in that multiple ticks are coalesced into a single tick if they occur between + /// calls to . Similarly, a call to will void any tick not yet consumed. + /// may only be used by one consumer at a time, and may be used concurrently with a single call to . + /// + public ValueTask WaitForNextTickAsync(CancellationToken cancellationToken = default) => + _state.WaitForNextTickAsync(this, cancellationToken); + + /// Stops the timer and releases associated managed resources. + /// + /// will cause an active wait with to complete with a value of false. + /// All subsequent invocations will produce a value of false. + /// + public void Dispose() + { + GC.SuppressFinalize(this); + _timer.Close(); + _state.Signal(stopping: true); + } + + ~PeriodicTimer() => Dispose(); + + /// Core implementation for the periodic timer. + private sealed class State : IValueTaskSource + { + /// The associated . + /// + /// This should refer to the parent instance only when there's an active waiter, and be null when there + /// isn't. The TimerQueueTimer in the PeriodicTimer strongly roots itself, and it references this State + /// object: + /// PeriodicTimer (finalizable) --ref--> TimerQueueTimer (rooted) --ref--> State --ref--> null + /// If this State object then references the PeriodicTimer, it creates a strongly-rooted cycle that prevents anything from + /// being GC'd: + /// PeriodicTimer (finalizable) --ref--> TimerQueueTimer (rooted) --ref--> State --v + /// ^--ref-------------------------------------------------------------------| + /// When this field is null, the cycle is broken, and dropping all references to the PeriodicTimer allows the + /// PeriodicTimer to be finalized and unroot the TimerQueueTimer. Thus, we keep this field set during + /// so that the timer roots any async continuation chain awaiting it, and then keep it unset otherwise so that everything + /// can be GC'd appropriately. + /// + private PeriodicTimer? _owner; + /// Core of the implementation. + private ManualResetValueTaskSourceCore _mrvtsc; + /// Cancellation registration for any active call. + private CancellationTokenRegistration _ctr; + /// Whether the timer has been stopped. + private bool _stopped; + /// Whether there's a pending notification to be received. This could be due to the timer firing, the timer being stopped, or cancellation being requested. + private bool _signaled; + /// Whether there's a call in flight. + private bool _activeWait; + + /// Wait for the next tick of the timer, or for the timer to be stopped. + public ValueTask WaitForNextTickAsync(PeriodicTimer owner, CancellationToken cancellationToken) + { + lock (this) + { + if (_activeWait) + { + // WaitForNextTickAsync should only be used by one consumer at a time. Failing to do so is an error. + ThrowHelper.ThrowInvalidOperationException(); + } + + // If cancellation has already been requested, short-circuit. + if (cancellationToken.IsCancellationRequested) + { + return ValueTask.FromCanceled(cancellationToken); + } + + // If the timer has a pending tick or has been stopped, we can complete synchronously. + if (_signaled) + { + // Reset the signal for subsequent consumers, but only if we're not stopped. Since. + // stopping the timer is one way, any subsequent calls should also complete synchronously + // with false, and thus we leave _signaled pinned at true. + if (!_stopped) + { + _signaled = false; + } + + return new ValueTask(!_stopped); + } + + Debug.Assert(!_stopped, "Unexpectedly stopped without _signaled being true."); + + // Set up for the wait and return a task that will be signaled when the + // timer fires, stop is called, or cancellation is requested. + _owner = owner; + _activeWait = true; + _ctr = cancellationToken.UnsafeRegister(static (state, cancellationToken) => ((State)state!).Signal(cancellationToken: cancellationToken), this); + + return new ValueTask(this, _mrvtsc.Version); + } + } + + /// Signal that the timer has either fired or been stopped. + public void Signal(bool stopping = false, CancellationToken cancellationToken = default) + { + bool completeTask = false; + + lock (this) + { + _stopped |= stopping; + if (!_signaled) + { + _signaled = true; + completeTask = _activeWait; + } + } + + if (completeTask) + { + if (cancellationToken.IsCancellationRequested) + { + // If cancellation is requested just before the UnsafeRegister call, it's possible this will end up being invoked + // as part of the WaitForNextTickAsync call and thus as part of holding the lock. The goal of completeTask + // was to escape that lock, so that we don't invoke any synchronous continuations from the ValueTask as part + // of completing _mrvtsc. However, in that case, we also haven't returned the ValueTask to the caller, so there + // won't be any continuations yet, which makes this safe. + _mrvtsc.SetException(ExceptionDispatchInfo.SetCurrentStackTrace(new OperationCanceledException(cancellationToken))); + } + else + { + Debug.Assert(!Monitor.IsEntered(this)); + _mrvtsc.SetResult(true); + } + } + } + + /// + bool IValueTaskSource.GetResult(short token) + { + // Dispose of the cancellation registration. This is done outside of the below lock in order + // to avoid a potential deadlock due to waiting for a concurrent cancellation callback that might + // in turn try to take the lock. For valid usage, GetResult is only called once _ctr has been + // successfully initialized before WaitForNextTickAsync returns to its synchronous caller, and + // there should be no race conditions accessing it, as concurrent consumption is invalid. If there + // is invalid usage, with GetResult used erroneously/concurrently, the worst that happens is cancellation + // may not take effect for the in-flight operation, with its registration erroneously disposed. + // Note we use Dispose rather than Unregister (which wouldn't risk deadlock) so that we know that thecancellation callback associated with this operation + // won't potentially still fire after we've completed this GetResult and a new operation + // has potentially started. + _ctr.Dispose(); + + lock (this) + { + try + { + _mrvtsc.GetResult(token); + } + finally + { + _mrvtsc.Reset(); + _ctr = default; + _activeWait = false; + _owner = null; + if (!_stopped) + { + _signaled = false; + } + } + + return !_stopped; + } + } + + /// + ValueTaskSourceStatus IValueTaskSource.GetStatus(short token) => _mrvtsc.GetStatus(token); + + /// + void IValueTaskSource.OnCompleted(Action continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags) => + _mrvtsc.OnCompleted(continuation, state, token, flags); + } + } +} diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 39a1cfc4b816c..4f9afb90503f3 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -11651,6 +11651,12 @@ public enum LazyThreadSafetyMode PublicationOnly = 1, ExecutionAndPublication = 2, } + public sealed class PeriodicTimer : System.IDisposable + { + public PeriodicTimer(System.TimeSpan period) { } + public System.Threading.Tasks.ValueTask WaitForNextTickAsync(System.Threading.CancellationToken cancellationToken = default) { throw null; } + public void Dispose() { } + } public static partial class Timeout { public const int Infinite = -1; diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj index 2ca6739e9c6c1..db3b3dcd1b5da 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj @@ -233,6 +233,7 @@ + diff --git a/src/libraries/System.Runtime/tests/System/Threading/PeriodicTimerTests.cs b/src/libraries/System.Runtime/tests/System/Threading/PeriodicTimerTests.cs new file mode 100644 index 0000000000000..92367b8a46746 --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/Threading/PeriodicTimerTests.cs @@ -0,0 +1,203 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using Xunit; + +namespace System.Threading.Tests +{ + public class PeriodicTimerTests + { + [Fact] + public void Ctor_InvalidArguments_Throws() + { + AssertExtensions.Throws("period", () => new PeriodicTimer(TimeSpan.FromMilliseconds(-1))); + AssertExtensions.Throws("period", () => new PeriodicTimer(TimeSpan.Zero)); + AssertExtensions.Throws("period", () => new PeriodicTimer(TimeSpan.FromMilliseconds(uint.MaxValue))); + } + + [Theory] + [InlineData(1)] + [InlineData(uint.MaxValue - 1)] + public void Ctor_ValidArguments_Succeeds(uint milliseconds) + { + using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(milliseconds)); + } + + [Fact] + public async Task Dispose_Idempotent() + { + var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(1)); + + Assert.True(await timer.WaitForNextTickAsync()); + + for (int i = 0; i < 2; i++) + { + timer.Dispose(); + Assert.False(timer.WaitForNextTickAsync().Result); + + ((IDisposable)timer).Dispose(); + Assert.False(timer.WaitForNextTickAsync().Result); + } + } + + [Fact] + public async Task WaitForNextTickAsync_TimerFires_ReturnsTrue() + { + using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(1)); + await Task.Delay(100); + for (int i = 0; i < 3; i++) + { + Assert.True(await timer.WaitForNextTickAsync()); + } + timer.Dispose(); + Assert.False(timer.WaitForNextTickAsync().Result); + } + + [Fact] + public async Task WaitForNextTickAsync_Dispose_ReturnsFalse() + { + using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(uint.MaxValue - 1)); + ValueTask task = timer.WaitForNextTickAsync(); + timer.Dispose(); + Assert.False(await task); + } + + [Fact] + public async Task WaitForNextTickAsync_ConcurrentDispose_ReturnsFalse() + { + using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(uint.MaxValue - 1)); + + _ = Task.Run(async delegate + { + await Task.Delay(1); + timer.Dispose(); + }); + + Assert.False(await timer.WaitForNextTickAsync()); + } + + [Fact] + public async Task WaitForNextTickAsync_ConcurrentDisposeAfterTicks_EventuallyReturnsFalse() + { + using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(1)); + + for (int i = 0; i < 5; i++) + { + Assert.True(await timer.WaitForNextTickAsync()); + } + + _ = Task.Run(async delegate + { + await Task.Delay(1); + timer.Dispose(); + }); + + while (await timer.WaitForNextTickAsync()); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsPreciseGcSupported))] + public void PeriodicTimer_NoActiveOperations_TimerNotRooted() + { + WeakReference timer = Create(); + + WaitForTimerToBeCollected(timer, expected: true); + + [MethodImpl(MethodImplOptions.NoInlining)] + static WeakReference Create() => + new WeakReference(new PeriodicTimer(TimeSpan.FromMilliseconds(1))); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsPreciseGcSupported))] + public async Task PeriodicTimer_ActiveOperations_TimerRooted() + { + (WeakReference timer, ValueTask task) = Create(); + + WaitForTimerToBeCollected(timer, expected: false); + + Assert.True(await task); + + WaitForTimerToBeCollected(timer, expected: true); + + [MethodImpl(MethodImplOptions.NoInlining)] + static (WeakReference, ValueTask) Create() + { + var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(1)); + ValueTask task = timer.WaitForNextTickAsync(); + return (new WeakReference(timer), task); + } + } + + [Fact] + public void WaitForNextTickAsync_WaitAlreadyInProgress_Throws() + { + using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(uint.MaxValue - 1)); + + ValueTask task = timer.WaitForNextTickAsync(); + Assert.False(task.IsCompleted); + + Assert.Throws(() => timer.WaitForNextTickAsync()); + + Assert.False(task.IsCompleted); + + timer.Dispose(); + Assert.True(task.IsCompleted); + Assert.False(task.Result); + } + + [Fact] + public void WaitForNextTickAsync_CanceledBeforeWait_CompletesSynchronously() + { + using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(uint.MaxValue - 1)); + + var cts = new CancellationTokenSource(); + cts.Cancel(); + + ValueTask task = timer.WaitForNextTickAsync(cts.Token); + Assert.True(task.IsCanceled); + Assert.Equal(cts.Token, Assert.ThrowsAny(() => task.Result).CancellationToken); + } + + [Fact] + public void WaitForNextTickAsync_CanceledAfterWait_CancelsOperation() + { + using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(uint.MaxValue - 1)); + + var cts = new CancellationTokenSource(); + + ValueTask task = timer.WaitForNextTickAsync(cts.Token); + cts.Cancel(); + + Assert.Equal(cts.Token, Assert.ThrowsAny(() => task.Result).CancellationToken); + } + + [Fact] + public async Task WaitForNextTickAsync_CanceledWaitThenWaitAgain_Succeeds() + { + using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(1)); + + ValueTask task = timer.WaitForNextTickAsync(new CancellationToken(true)); + Assert.ThrowsAny(() => task.Result); + + var cts = new CancellationTokenSource(); + task = timer.WaitForNextTickAsync(cts.Token); + cts.Cancel(); + Assert.Equal(cts.Token, Assert.ThrowsAny(() => task.Result).CancellationToken); + + for (int i = 0; i < 10; i++) + { + Assert.True(await timer.WaitForNextTickAsync()); + } + } + + private static void WaitForTimerToBeCollected(WeakReference timer, bool expected) + { + Assert.Equal(expected, SpinWait.SpinUntil(() => + { + GC.Collect(); + return !timer.TryGetTarget(out _); + }, TimeSpan.FromSeconds(expected ? 5 : 0.5))); + } + } +} From 2ac023c8e9df61fe4557212cbd13956042fb47c5 Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Thu, 24 Jun 2021 16:31:07 +0200 Subject: [PATCH 102/107] process more TLS frames at one when available (#50815) * process more TLS frames when available * add SslStream.Implementation.cs * remove extra comment * add back DefaultRequestHeaders_SentUnparsed * feedback from review * fix winhttp * fix linker test * feedback from review * feedback from review * feedback from review * make EnsureFullTlsFrame async * feedback from review --- .../Interop.Ssl.cs | 2 +- .../Interop.OpenSsl.cs | 3 - .../Net/Http/SchSendAuxRecordHttpTest.cs | 127 -------- .../FunctionalTests/PlatformHandlerTest.cs | 12 - ...ttp.WinHttpHandler.Functional.Tests.csproj | 2 - .../FunctionalTests/SocketsHttpHandlerTest.cs | 5 - .../System.Net.Http.Functional.Tests.csproj | 2 - .../src/System/Net/Logging/NetEventSource.cs | 2 +- .../src/System/Net/Security/SecureChannel.cs | 20 +- .../Net/Security/SslStream.Implementation.cs | 298 ++++++++++++------ .../Net/Security/SslStreamPal.Android.cs | 13 +- .../System/Net/Security/SslStreamPal.OSX.cs | 18 +- .../System/Net/Security/SslStreamPal.Unix.cs | 7 +- .../Net/Security/SslStreamPal.Windows.cs | 8 +- .../src/System/Net/Security/TlsFrameHelper.cs | 5 + .../SslStreamSchSendAuxRecordTest.cs | 125 -------- .../System.Net.Security.Tests.csproj | 1 - 17 files changed, 239 insertions(+), 411 deletions(-) delete mode 100644 src/libraries/Common/tests/System/Net/Http/SchSendAuxRecordHttpTest.cs delete mode 100644 src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSchSendAuxRecordTest.cs 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 762d352a6f63a..6f0c53bac516c 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 @@ -166,7 +166,7 @@ private static unsafe extern PAL_SSLStreamStatus SSLStreamRead( out int bytesRead); internal static unsafe PAL_SSLStreamStatus SSLStreamRead( SafeSslHandle sslHandle, - Span buffer, + ReadOnlySpan buffer, out int bytesRead) { fixed (byte* bufferPtr = buffer) diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs index 42276ab2b045b..604ac8b4c7980 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs @@ -2,19 +2,16 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Buffers; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; -using System.Net.Http; using System.Net.Security; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security.Authentication; using System.Security.Authentication.ExtendedProtection; using System.Security.Cryptography; -using System.Security.Cryptography.X509Certificates; using Microsoft.Win32.SafeHandles; internal static partial class Interop diff --git a/src/libraries/Common/tests/System/Net/Http/SchSendAuxRecordHttpTest.cs b/src/libraries/Common/tests/System/Net/Http/SchSendAuxRecordHttpTest.cs deleted file mode 100644 index 0348db4d357ba..0000000000000 --- a/src/libraries/Common/tests/System/Net/Http/SchSendAuxRecordHttpTest.cs +++ /dev/null @@ -1,127 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Net.Test.Common; -using System.Security.Authentication; -using System.Threading.Tasks; - -using Xunit; -using Xunit.Abstractions; - -namespace System.Net.Http.Functional.Tests -{ -#if WINHTTPHANDLER_TEST - using HttpClientHandler = System.Net.Http.WinHttpClientHandler; -#endif - - public abstract class SchSendAuxRecordHttpTest : HttpClientHandlerTestBase - { - public SchSendAuxRecordHttpTest(ITestOutputHelper output) : base(output) { } - - private class CircularBuffer - { - public CircularBuffer(int size) => _buffer = new char[size]; - - private char[] _buffer; - private int _lastBytesWriteIndex = 0; - private int _size = 0; - - public void Add(string value) - { - foreach (char ch in value) - { - _buffer[_lastBytesWriteIndex] = ch; - - _lastBytesWriteIndex = ++_lastBytesWriteIndex % _buffer.Length; - _size = Math.Min(_buffer.Length, ++_size); - } - } - - public bool Equals(string value) - { - if (value.Length != _size) - return false; - - for (int i = 0; i < _size; i++) - { - if (_buffer[(_lastBytesWriteIndex + i) % _buffer.Length] != value[i]) - return false; - } - - return true; - } - } - - [Fact] - [PlatformSpecific(TestPlatforms.Windows)] - public async Task HttpClient_ClientUsesAuxRecord_Ok() - { - var options = new HttpsTestServer.Options(); - options.AllowedProtocols = SslProtocols.Tls; - - using (var server = new HttpsTestServer(options)) - using (HttpClientHandler handler = CreateHttpClientHandler()) - using (HttpClient client = CreateHttpClient(handler)) - { - handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates; - server.Start(); - - var tasks = new Task[2]; - - bool serverAuxRecordDetected = false; - bool serverAuxRecordDetectedInconclusive = false; - int serverTotalBytesReceived = 0; - int serverChunks = 0; - - CircularBuffer buffer = new CircularBuffer(4); - - tasks[0] = server.AcceptHttpsClientAsync((requestString) => - { - - buffer.Add(requestString); - - serverTotalBytesReceived += requestString.Length; - - if (serverTotalBytesReceived == 1 && serverChunks == 0) - { - serverAuxRecordDetected = true; - } - - serverChunks++; - - // Test is inconclusive if any non-CBC cipher is used: - if (server.Stream.CipherAlgorithm == CipherAlgorithmType.None || - server.Stream.CipherAlgorithm == CipherAlgorithmType.Null || - server.Stream.CipherAlgorithm == CipherAlgorithmType.Rc4) - { - serverAuxRecordDetectedInconclusive = true; - } - - // Detect end of HTML request - if (buffer.Equals("\r\n\r\n")) - { - return Task.FromResult(HttpsTestServer.Options.DefaultResponseString); - } - else - { - return Task.FromResult(null); - } - }); - - string requestUriString = "https://localhost:" + server.Port.ToString(); - tasks[1] = client.GetStringAsync(requestUriString); - - await tasks.WhenAllOrAnyFailed(15 * 1000); - - if (serverAuxRecordDetectedInconclusive) - { - _output.WriteLine("Test inconclusive: The Operating system preferred a non-CBC or Null cipher."); - } - else - { - Assert.True(serverAuxRecordDetected, "Server reports: Client auxiliary record not detected."); - } - } - } - } -} diff --git a/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/PlatformHandlerTest.cs b/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/PlatformHandlerTest.cs index 9e384c292c8a0..9e6d360d9ac7a 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/PlatformHandlerTest.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/PlatformHandlerTest.cs @@ -135,11 +135,6 @@ public sealed class PlatformHandler_HttpClientHandler_Proxy_Test : HttpClientHan public PlatformHandler_HttpClientHandler_Proxy_Test(ITestOutputHelper output) : base(output) { } } - public sealed class PlatformHandler_SchSendAuxRecordHttpTest : SchSendAuxRecordHttpTest - { - public PlatformHandler_SchSendAuxRecordHttpTest(ITestOutputHelper output) : base(output) { } - } - public sealed class PlatformHandler_HttpClientHandlerTest : HttpClientHandlerTest { public PlatformHandler_HttpClientHandlerTest(ITestOutputHelper output) : base(output) { } @@ -299,13 +294,6 @@ public sealed class PlatformHandler_HttpClientHandler_Proxy_Http2_Test : HttpCli public PlatformHandler_HttpClientHandler_Proxy_Http2_Test(ITestOutputHelper output) : base(output) { } } - public sealed class PlatformHandler_SchSendAuxRecordHttp_Http2_Test : SchSendAuxRecordHttpTest - { - protected override Version UseVersion => HttpVersion20.Value; - - public PlatformHandler_SchSendAuxRecordHttp_Http2_Test(ITestOutputHelper output) : base(output) { } - } - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsWindows10Version1607OrGreater))] public sealed class PlatformHandler_HttpClientHandler_Http2_Test : HttpClientHandlerTest { diff --git a/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/System.Net.Http.WinHttpHandler.Functional.Tests.csproj b/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/System.Net.Http.WinHttpHandler.Functional.Tests.csproj index c72ec6b0acc11..4b6596d143222 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/System.Net.Http.WinHttpHandler.Functional.Tests.csproj +++ b/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/System.Net.Http.WinHttpHandler.Functional.Tests.csproj @@ -127,8 +127,6 @@ Link="Common\System\Net\Http\RepeatedFlushContent.cs" /> - - diff --git a/src/libraries/System.Net.Security/src/System/Net/Logging/NetEventSource.cs b/src/libraries/System.Net.Security/src/System/Net/Logging/NetEventSource.cs index 63af2603db476..f71c6a9054478 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Logging/NetEventSource.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Logging/NetEventSource.cs @@ -13,7 +13,7 @@ internal sealed partial class NetEventSource : EventSource /// The buffer to be logged. /// The calling member. [NonEvent] - public static void DumpBuffer(object thisOrContextObject, ReadOnlyMemory buffer, [CallerMemberName] string? memberName = null) + public static void DumpBuffer(object thisOrContextObject, ReadOnlySpan buffer, [CallerMemberName] string? memberName = null) { if (Log.IsEnabled()) { diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs index 5d234b5886215..2cbc44c585739 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs @@ -879,7 +879,7 @@ is non-zero. --*/ internal SecurityStatusPal Encrypt(ReadOnlyMemory buffer, ref byte[] output, out int resultSize) { - if (NetEventSource.Log.IsEnabled()) NetEventSource.DumpBuffer(this, buffer); + if (NetEventSource.Log.IsEnabled()) NetEventSource.DumpBuffer(this, buffer.Span); byte[] writeBuffer = output; @@ -903,24 +903,12 @@ internal SecurityStatusPal Encrypt(ReadOnlyMemory buffer, ref byte[] outpu return secStatus; } - internal SecurityStatusPal Decrypt(byte[]? payload, ref int offset, ref int count) + internal SecurityStatusPal Decrypt(Span buffer, out int outputOffset, out int outputCount) { - if ((uint)offset > (uint)(payload == null ? 0 : payload.Length)) - { - Debug.Fail("Argument 'offset' out of range."); - throw new ArgumentOutOfRangeException(nameof(offset)); - } - - if ((uint)count > (uint)(payload == null ? 0 : payload.Length - offset)) - { - Debug.Fail("Argument 'count' out of range."); - throw new ArgumentOutOfRangeException(nameof(count)); - } - - SecurityStatusPal status = SslStreamPal.DecryptMessage(_securityContext!, payload!, ref offset, ref count); + SecurityStatusPal status = SslStreamPal.DecryptMessage(_securityContext!, buffer, out outputOffset, out outputCount); if (NetEventSource.Log.IsEnabled() && status.ErrorCode == SecurityStatusPalErrorCode.OK) { - NetEventSource.DumpBuffer(this, payload!, offset, count); + NetEventSource.DumpBuffer(this, buffer.Slice(outputOffset, outputCount)); } return status; diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs index 81f2c92880b55..17858bb0e68b6 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs @@ -43,6 +43,7 @@ private enum Framing private const int ReadBufferSize = 4096 * 4 + FrameOverhead; // We read in 16K chunks + headers. private const int InitialHandshakeBufferSize = 4096 + FrameOverhead; // try to fit at least 4K ServerCertificate private ArrayBuffer _handshakeBuffer; + private bool _receivedEOF; // Used by Telemetry to ensure we log connection close exactly once // 0 = no handshake @@ -183,17 +184,6 @@ private SecurityStatusPal EncryptData(ReadOnlyMemory buffer, ref byte[] ou } } - private SecurityStatusPal DecryptData() - { - ThrowIfExceptionalOrNotAuthenticated(); - return PrivateDecryptData(_internalBuffer, ref _decryptedBytesOffset, ref _decryptedBytesCount); - } - - private SecurityStatusPal PrivateDecryptData(byte[]? buffer, ref int offset, ref int count) - { - return _context!.Decrypt(buffer, ref offset, ref count); - } - // // This method assumes that a SSPI context is already in a good shape. // For example it is either a fresh context or already authenticated context that needs renegotiation. @@ -813,6 +803,117 @@ private void ReturnReadBufferIfEmpty() _decryptedBytesCount = 0; _decryptedBytesOffset = 0; } + else if (_decryptedBytesCount == 0) + { + _decryptedBytesOffset = 0; + } + } + + + private bool HaveFullTlsFrame(out int frameSize) + { + if (_internalBufferCount < SecureChannel.ReadHeaderSize) + { + frameSize = int.MaxValue; + return false; + } + + frameSize = GetFrameSize(_internalBuffer.AsSpan(_internalOffset)); + return _internalBufferCount >= frameSize; + } + + + private async ValueTask EnsureFullTlsFrameAsync(TIOAdapter adapter) + where TIOAdapter : IReadWriteAdapter + { + int frameSize; + if (HaveFullTlsFrame(out frameSize)) + { + return frameSize; + } + + // We may have enough space to complete frame, but we may still do extra IO if the frame is small. + // So we will attempt larger read - that is trade of with extra copy. + // This may be updated at some point based on size of existing chunk, rented buffer and size of 'buffer'. + ResetReadBuffer(); + + // _internalOffset is 0 after ResetReadBuffer and we use _internalBufferCount to determined where to read. + while (_internalBufferCount < frameSize) + { + // We either don't have full frame or we don't have enough data to even determine the size. + int bytesRead = await adapter.ReadAsync(_internalBuffer.AsMemory(_internalBufferCount)).ConfigureAwait(false); + if (bytesRead == 0) + { + if (_internalBufferCount != 0) + { + // we got EOF in middle of TLS frame. Treat that as error. + throw new IOException(SR.net_io_eof); + } + + return 0; + } + + _internalBufferCount += bytesRead; + if (frameSize == int.MaxValue && _internalBufferCount > SecureChannel.ReadHeaderSize) + { + // recalculate frame size if needed e.g. we could not get it before. + frameSize = GetFrameSize(_internalBuffer.AsSpan(_internalOffset)); + } + } + + return frameSize; + } + + private SecurityStatusPal DecryptData(int frameSize) + { + Debug.Assert(_decryptedBytesCount == 0); + + // Set _decryptedBytesOffset/Count to the current frame we have (including header) + // DecryptData will decrypt in-place and modify these to point to the actual decrypted data, which may be smaller. + _decryptedBytesOffset = _internalOffset; + _decryptedBytesCount = frameSize; + SecurityStatusPal status; + + lock (_handshakeLock) + { + ThrowIfExceptionalOrNotAuthenticated(); + status = _context!.Decrypt(new Span(_internalBuffer, _internalOffset, frameSize), out int decryptedOffset, out int decryptedCount); + _decryptedBytesCount = decryptedCount; + if (decryptedCount > 0) + { + _decryptedBytesOffset = _internalOffset + decryptedOffset; + } + + if (status.ErrorCode == SecurityStatusPalErrorCode.Renegotiate) + { + // The status indicates that peer wants to renegotiate. (Windows only) + // In practice, there can be some other reasons too - like TLS1.3 session creation + // of alert handling. We need to pass the data to lsass and it is not safe to do parallel + // write any more as that can change TLS state and the EncryptData() can fail in strange ways. + + // To handle this we call DecryptData() under lock and we create TCS waiter. + // EncryptData() checks that under same lock and if it exist it will not call low-level crypto. + // Instead it will wait synchronously or asynchronously and it will try again after the wait. + // The result will be set when ReplyOnReAuthenticationAsync() is finished e.g. lsass business is over. + // If that happen before EncryptData() runs, _handshakeWaiter will be set to null + // and EncryptData() will work normally e.g. no waiting, just exclusion with DecryptData() + + + if (_sslAuthenticationOptions!.AllowRenegotiation || SslProtocol == SslProtocols.Tls13 || _nestedAuth != 0) + { + // create TCS only if we plan to proceed. If not, we will throw later outside of the lock. + // Tls1.3 does not have renegotiation. However on Windows this error code is used + // for session management e.g. anything lsass needs to see. + // We also allow it when explicitly requested using RenegotiateAsync(). + _handshakeWaiter = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + } + } + } + + // Treat the bytes we just decrypted as consumed + ConsumeBufferedBytes(frameSize); + + return status; } private async ValueTask ReadAsyncInternal(TIOAdapter adapter, Memory buffer, bool renegotiation = false) @@ -826,109 +927,63 @@ private async ValueTask ReadAsyncInternal(TIOAdapter adapter, M } } + ThrowIfExceptionalOrNotAuthenticated(); + Debug.Assert(_internalBuffer is null || _internalBufferCount > 0 || _decryptedBytesCount > 0, "_internalBuffer allocated when no data is buffered."); + int processedLength = 0; + int payloadBytes = 0; try { - while (true) + if (_decryptedBytesCount != 0) { - if (_decryptedBytesCount != 0) + if (renegotiation) { - if (renegotiation) - { - throw new InvalidOperationException(SR.net_ssl_renegotiate_data); - } - - return CopyDecryptedData(buffer); + throw new InvalidOperationException(SR.net_ssl_renegotiate_data); } - if (buffer.Length == 0 && _internalBuffer is null && !renegotiation) + processedLength = CopyDecryptedData(buffer); + if (processedLength == buffer.Length || !HaveFullTlsFrame(out payloadBytes)) { - // User requested a zero-byte read, and we have no data available in the buffer for processing. - // This zero-byte read indicates their desire to trade off the extra cost of a zero-byte read - // for reduced memory consumption when data is not immediately available. - // So, we will issue our own zero-byte read against the underlying stream and defer buffer allocation - // until data is actually available from the underlying stream. - // Note that if the underlying stream does not supporting blocking on zero byte reads, then this will - // complete immediately and won't save any memory, but will still function correctly. - await adapter.ReadAsync(Memory.Empty).ConfigureAwait(false); + // We either filled whole buffer or used all buffered frames. + return processedLength; } - ResetReadBuffer(); - - // Read the next frame header. - if (_internalBufferCount < SecureChannel.ReadHeaderSize) - { - // We don't have enough bytes buffered, so issue an initial read to try to get enough. This is - // done in this method both to better consolidate error handling logic (the first read is the special - // case that needs to differentiate reading 0 from > 0, and everything else needs to throw if it - // doesn't read enough), and to minimize the chances that in the common case the FillBufferAsync - // helper needs to yield and allocate a state machine. - int readBytes = await adapter.ReadAsync(_internalBuffer.AsMemory(_internalBufferCount)).ConfigureAwait(false); - if (readBytes == 0) - { - return 0; - } - - _internalBufferCount += readBytes; - if (_internalBufferCount < SecureChannel.ReadHeaderSize) - { - await FillBufferAsync(adapter, SecureChannel.ReadHeaderSize).ConfigureAwait(false); - } - } - Debug.Assert(_internalBufferCount >= SecureChannel.ReadHeaderSize); + buffer = buffer.Slice(processedLength); + } - // Parse the frame header to determine the payload size (which includes the header size). - int payloadBytes = GetFrameSize(_internalBuffer.AsSpan(_internalOffset)); - if (payloadBytes < 0) - { - throw new IOException(SR.net_frame_read_size); - } + if (_receivedEOF) + { + Debug.Assert(_internalBufferCount == 0); + // We received EOF during previous read but had buffered data to return. + return 0; + } - // Read in the rest of the payload if we don't have it. - if (_internalBufferCount < payloadBytes) - { - await FillBufferAsync(adapter, payloadBytes).ConfigureAwait(false); - } + if (buffer.Length == 0 && _internalBuffer is null) + { + // User requested a zero-byte read, and we have no data available in the buffer for processing. + // This zero-byte read indicates their desire to trade off the extra cost of a zero-byte read + // for reduced memory consumption when data is not immediately available. + // So, we will issue our own zero-byte read against the underlying stream and defer buffer allocation + // until data is actually available from the underlying stream. + // Note that if the underlying stream does not supporting blocking on zero byte reads, then this will + // complete immediately and won't save any memory, but will still function correctly. + await adapter.ReadAsync(Memory.Empty).ConfigureAwait(false); + } - // Set _decrytpedBytesOffset/Count to the current frame we have (including header) - // DecryptData will decrypt in-place and modify these to point to the actual decrypted data, which may be smaller. - _decryptedBytesOffset = _internalOffset; - _decryptedBytesCount = payloadBytes; + Debug.Assert(_decryptedBytesCount == 0); + Debug.Assert(_decryptedBytesOffset == 0); - SecurityStatusPal status; - lock (_handshakeLock) + while (true) + { + payloadBytes = await EnsureFullTlsFrameAsync(adapter).ConfigureAwait(false); + if (payloadBytes == 0) { - status = DecryptData(); - if (status.ErrorCode == SecurityStatusPalErrorCode.Renegotiate) - { - // The status indicates that peer wants to renegotiate. (Windows only) - // In practice, there can be some other reasons too - like TLS1.3 session creation - // of alert handling. We need to pass the data to lsass and it is not safe to do parallel - // write any more as that can change TLS state and the EncryptData() can fail in strange ways. - - // To handle this we call DecryptData() under lock and we create TCS waiter. - // EncryptData() checks that under same lock and if it exist it will not call low-level crypto. - // Instead it will wait synchronously or asynchronously and it will try again after the wait. - // The result will be set when ReplyOnReAuthenticationAsync() is finished e.g. lsass business is over. - // If that happen before EncryptData() runs, _handshakeWaiter will be set to null - // and EncryptData() will work normally e.g. no waiting, just exclusion with DecryptData() - - if (_sslAuthenticationOptions!.AllowRenegotiation || SslProtocol == SslProtocols.Tls13 || _nestedAuth != 0) - { - // create TCS only if we plan to proceed. If not, we will throw in block bellow outside of the lock. - // Tls1.3 does not have renegotiation. However on Windows this error code is used - // for session management e.g. anything lsass needs to see. - // We also allow it when explicitly requested using RenegotiateAsync(). - _handshakeWaiter = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - } - } + _receivedEOF = true; + break; } - // Treat the bytes we just decrypted as consumed - // Note, we won't do another buffer read until the decrypted bytes are processed - ConsumeBufferedBytes(payloadBytes); - + SecurityStatusPal status = DecryptData(payloadBytes); if (status.ErrorCode != SecurityStatusPalErrorCode.OK) { byte[]? extraBuffer = null; @@ -962,12 +1017,50 @@ private async ValueTask ReadAsyncInternal(TIOAdapter adapter, M if (status.ErrorCode == SecurityStatusPalErrorCode.ContextExpired) { - return 0; + _receivedEOF = true; + break; } throw new IOException(SR.net_io_decrypt, SslStreamPal.GetException(status)); } + + if (_decryptedBytesCount > 0) + { + // This will either copy data from rented buffer or adjust final buffer as needed. + // In both cases _decryptedBytesOffset and _decryptedBytesCount will be updated as needed. + int copyLength = CopyDecryptedData(buffer); + processedLength += copyLength; + if (copyLength == buffer.Length) + { + // We have more decrypted data after we filled provided buffer. + break; + } + + buffer = buffer.Slice(copyLength); + } + + if (processedLength == 0) + { + // We did not get any real data so far. + continue; + } + + if (!HaveFullTlsFrame(out payloadBytes)) + { + // We don't have another frame to process but we have some data to return to caller. + break; + } + + TlsFrameHelper.TryGetFrameHeader(_internalBuffer.AsSpan(_internalOffset), ref _lastFrame.Header); + if (_lastFrame.Header.Type != TlsContentType.AppData) + { + // Alerts, handshake and anything else will be processed separately. + // This may not be necessary but it improves compatibility with older versions. + break; + } } + + return processedLength; } catch (Exception e) { @@ -1101,6 +1194,10 @@ private void ConsumeBufferedBytes(int byteCount) _internalOffset += byteCount; _internalBufferCount -= byteCount; + if (_internalBufferCount == 0) + { + _internalOffset = 0; + } } private int CopyDecryptedData(Memory buffer) @@ -1116,6 +1213,11 @@ private int CopyDecryptedData(Memory buffer) _decryptedBytesCount -= copyBytes; } + if (_decryptedBytesCount == 0) + { + _decryptedBytesOffset = 0; + } + return copyBytes; } @@ -1333,7 +1435,7 @@ private int GetFrameSize(ReadOnlySpan buffer) payloadSize = ((buffer[3] << 8) | buffer[4]) + 5; break; default: - break; + throw new IOException(SR.net_frame_read_size); } return payloadSize; diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs index 24f1e8e0e01b6..27c854c99168e 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs @@ -112,17 +112,20 @@ public static SecurityStatusPal EncryptMessage( public static SecurityStatusPal DecryptMessage( SafeDeleteSslContext securityContext, - byte[] buffer, - ref int offset, - ref int count) + Span buffer, + out int offset, + out int count) { + offset = 0; + count = 0; + try { SafeSslHandle sslHandle = securityContext.SslContext; - securityContext.Write(buffer.AsSpan(offset, count)); + securityContext.Write(buffer); - PAL_SSLStreamStatus ret = Interop.AndroidCrypto.SSLStreamRead(sslHandle, buffer.AsSpan(offset, count), out int read); + PAL_SSLStreamStatus ret = Interop.AndroidCrypto.SSLStreamRead(sslHandle, buffer, out int read); if (ret == PAL_SSLStreamStatus.Error) return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError); diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs index 53c4d12c99ab8..0414082781c1e 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs @@ -143,22 +143,23 @@ public static SecurityStatusPal EncryptMessage( public static SecurityStatusPal DecryptMessage( SafeDeleteSslContext securityContext, - byte[] buffer, - ref int offset, - ref int count) + Span buffer, + out int offset, + out int count) { + offset = 0; + count = 0; + try { SafeSslHandle sslHandle = securityContext.SslContext; - - securityContext.Write(buffer.AsSpan(offset, count)); + securityContext.Write(buffer); unsafe { - fixed (byte* offsetInput = &buffer[offset]) + fixed (byte* ptr = buffer) { - PAL_TlsIo status = Interop.AppleCrypto.SslRead(sslHandle, offsetInput, count, out int written); - + PAL_TlsIo status = Interop.AppleCrypto.SslRead(sslHandle, ptr, buffer.Length, out int written); if (status < 0) { return new SecurityStatusPal( @@ -167,6 +168,7 @@ public static SecurityStatusPal DecryptMessage( } count = written; + offset = 0; switch (status) { diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs index 98a21e128569c..020456131702f 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs @@ -62,11 +62,14 @@ public static SecurityStatusPal EncryptMessage(SafeDeleteSslContext securityCont } } - public static SecurityStatusPal DecryptMessage(SafeDeleteSslContext securityContext, byte[] buffer, ref int offset, ref int count) + public static SecurityStatusPal DecryptMessage(SafeDeleteSslContext securityContext, Span buffer, out int offset, out int count) { + offset = 0; + count = 0; + try { - int resultSize = Interop.OpenSsl.Decrypt(securityContext.SslContext, new Span(buffer, offset, count), out Interop.Ssl.SslErrorCode errorCode); + int resultSize = Interop.OpenSsl.Decrypt(securityContext.SslContext, buffer, out Interop.Ssl.SslErrorCode errorCode); SecurityStatusPal retVal = MapNativeErrorCode(errorCode); diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs index 59a781b470a9e..4060296c4ed87 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs @@ -313,7 +313,7 @@ public static unsafe SecurityStatusPal EncryptMessage(SafeDeleteSslContext secur } } - public static unsafe SecurityStatusPal DecryptMessage(SafeDeleteSslContext? securityContext, byte[] buffer, ref int offset, ref int count) + public static unsafe SecurityStatusPal DecryptMessage(SafeDeleteSslContext? securityContext, Span buffer, out int offset, out int count) { const int NumSecBuffers = 4; // data + empty + empty + empty fixed (byte* bufferPtr = buffer) @@ -321,8 +321,8 @@ public static unsafe SecurityStatusPal DecryptMessage(SafeDeleteSslContext? secu Interop.SspiCli.SecBuffer* unmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[NumSecBuffers]; Interop.SspiCli.SecBuffer* dataBuffer = &unmanagedBuffer[0]; dataBuffer->BufferType = SecurityBufferType.SECBUFFER_DATA; - dataBuffer->pvBuffer = (IntPtr)bufferPtr + offset; - dataBuffer->cbBuffer = count; + dataBuffer->pvBuffer = (IntPtr)bufferPtr; + dataBuffer->cbBuffer = buffer.Length; for (int i = 1; i < NumSecBuffers; i++) { @@ -341,6 +341,7 @@ public static unsafe SecurityStatusPal DecryptMessage(SafeDeleteSslContext? secu // Decrypt may repopulate the sec buffers, likely with header + data + trailer + empty. // We need to find the data. count = 0; + offset = 0; for (int i = 0; i < NumSecBuffers; i++) { // Successfully decoded data and placed it at the following position in the buffer, @@ -351,6 +352,7 @@ public static unsafe SecurityStatusPal DecryptMessage(SafeDeleteSslContext? secu offset = (int)((byte*)unmanagedBuffer[i].pvBuffer - bufferPtr); count = unmanagedBuffer[i].cbBuffer; + // output is ignored on Windows. We always decrypt in place and we set outputOffset to indicate where the data start. Debug.Assert(offset >= 0 && count >= 0, $"Expected offset and count greater than 0, got {offset} and {count}"); Debug.Assert(checked(offset + count) <= buffer.Length, $"Expected offset+count <= buffer.Length, got {offset}+{count}>={buffer.Length}"); diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/TlsFrameHelper.cs b/src/libraries/System.Net.Security/src/System/Net/Security/TlsFrameHelper.cs index 8104387dc07ff..8f5b5ed2a07c5 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/TlsFrameHelper.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/TlsFrameHelper.cs @@ -90,6 +90,11 @@ internal struct TlsFrameHeader public int Length; public override string ToString() => $"{Version}:{Type}[{Length}]"; + + public int GetFrameSize() + { + return Length + TlsFrameHelper.HeaderSize; + } } internal static class TlsFrameHelper diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSchSendAuxRecordTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSchSendAuxRecordTest.cs deleted file mode 100644 index 2389188bbef5e..0000000000000 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSchSendAuxRecordTest.cs +++ /dev/null @@ -1,125 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Net.Test.Common; -using System.Security.Authentication; -using System.Threading.Tasks; - -using Xunit; -using Xunit.Abstractions; - -namespace System.Net.Security.Tests -{ - using Configuration = System.Net.Test.Common.Configuration; - - public class SchSendAuxRecordTest - { - readonly ITestOutputHelper _output; - - public SchSendAuxRecordTest(ITestOutputHelper output) - { - _output = output; - } - - [Fact] - [PlatformSpecific(TestPlatforms.Windows)] - public async Task SslStream_ClientAndServerUsesAuxRecord_Ok() - { - using (var server = new HttpsTestServer()) - { - server.Start(); - - var tasks = new Task[2]; - - bool serverAuxRecordDetected = false; - bool serverAuxRecordDetectedInconclusive = false; - int serverTotalBytesReceived = 0; - int serverChunks = 0; - - tasks[0] = server.AcceptHttpsClientAsync((requestString) => - { - serverTotalBytesReceived += requestString.Length; - - if (serverTotalBytesReceived == 1 && serverChunks == 0) - { - serverAuxRecordDetected = true; - } - - serverChunks++; - - // Test is inconclusive if any non-CBC cipher is used: - if (server.Stream.CipherAlgorithm == CipherAlgorithmType.None || - server.Stream.CipherAlgorithm == CipherAlgorithmType.Null || - server.Stream.CipherAlgorithm == CipherAlgorithmType.Rc4) - { - serverAuxRecordDetectedInconclusive = true; - } - - if (serverTotalBytesReceived < 5) - { - return Task.FromResult(null); - } - else - { - return Task.FromResult(HttpsTestServer.Options.DefaultResponseString); - } - }); - - - var clientOptions = new HttpsTestClient.Options(new IPEndPoint(IPAddress.Loopback, server.Port)); - clientOptions.AllowedProtocols = SslProtocols.Tls; - - clientOptions.IgnoreSslPolicyErrors = - SslPolicyErrors.RemoteCertificateNameMismatch | SslPolicyErrors.RemoteCertificateChainErrors; - - var client = new HttpsTestClient(clientOptions); - - bool clientAuxRecordDetected = false; - bool clientAuxRecordDetectedInconclusive = false; - int clientTotalBytesReceived = 0; - int clientChunks = 0; - - tasks[1] = client.HttpsRequestAsync((responseString) => - { - if (responseString == null) - { - string requestString = string.Format( - HttpsTestClient.Options.DefaultRequestStringTemplate, - clientOptions.ServerName); - - return Task.FromResult(requestString); - } - - clientTotalBytesReceived += responseString.Length; - - if (clientTotalBytesReceived == 1 && clientChunks == 0) - { - clientAuxRecordDetected = true; - } - - // Test is inconclusive if any non-CBC cipher is used: - if (client.Stream.CipherAlgorithm == CipherAlgorithmType.None || - client.Stream.CipherAlgorithm == CipherAlgorithmType.Null || - client.Stream.CipherAlgorithm == CipherAlgorithmType.Rc4) - { - clientAuxRecordDetectedInconclusive = true; - } - - return Task.FromResult(null); - }); - - await Task.WhenAll(tasks).WaitAsync(TestConfiguration.PassingTestTimeout); - - if (serverAuxRecordDetectedInconclusive || clientAuxRecordDetectedInconclusive) - { - _output.WriteLine("Test inconclusive: The Operating system preferred a non-CBC or Null cipher."); - } - else - { - Assert.True(serverAuxRecordDetected, "Server reports: Client auxiliary record not detected."); - Assert.True(clientAuxRecordDetected, "Client reports: Server auxiliary record not detected."); - } - } - } - } -} diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj b/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj index bb131408de325..3dc537ad91deb 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj @@ -100,7 +100,6 @@ - From 458bb9efd1de6544aa2a1645ba699ef7e59889df Mon Sep 17 00:00:00 2001 From: Viktor Hofer Date: Thu, 24 Jun 2021 16:46:39 +0200 Subject: [PATCH 103/107] Fix compiler references when building inside VS (#54614) If for a source project a contract project exists, then the contract project's TargetPath should be passed to the compiler. This is handled by the SDK by default when `ProduceReferenceAssembly` is true. As dotnet/runtime doesn't use the `ProduceReferenceAssembly` feature yet, a custom target adds the necessary `ReferenceAssembly` metadata to the `TargetPathWithTargetPlatformMoniker` item which then is transformed to references for the compiler. That works fine on the CLI as the `GetTargetPathWithTargetPlatformMoniker` target runs after the ProjectReference to the ContractProject is resolved and its target path is available. Inside VS the target ordering is different and the `ResolvedMatchingContract` item was empty as the ProjectReference to the contract wasn't yet resolved. The fix for that is to add a dependency onto the `ResolveProjectReferences` target to guarantee that the `ResolvedMatchingContract` item is populated in time. Noticed this when the build of System.ComponentModel.Composition.Registration failed because the implementation assembly of System.ComponentModel.Composition was passed to the compiler instead of the reference assembly. --- eng/resolveContract.targets | 1 + 1 file changed, 1 insertion(+) diff --git a/eng/resolveContract.targets b/eng/resolveContract.targets index d98ef8dffb7bd..8c99a9059defb 100644 --- a/eng/resolveContract.targets +++ b/eng/resolveContract.targets @@ -25,6 +25,7 @@ From 055a38a9062c4c9ec3a811eec02f7e37fb1fbbd5 Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Thu, 24 Jun 2021 17:20:26 +0200 Subject: [PATCH 104/107] [wasm] Bump emscripten to 2.0.23 (#53603) Bumps emscripten to 2.0.23 The Browser AOT tests now use `-Wl,-lto-O0` option to reduce memory usage of `wasm-ld` tool, which was in some cases going over avaiable 8GB on helix machines. * Revert "Add ActiveIssue to the MemoryMappedFiles tests" This reverts commit ec1ae530606ef1061680600fc046226cc1c4cbc3. * Revert "Add ActiveIssue attr to the FileSystem tests" This reverts commit 356b3ff2a703980ac01b9df697a594e8c341c436. * Bump emscripten version to 2.0.23 * Use newer docker images with 2.0.23 * Update docs * Use 2.0.23 emscripten nuget packages * Revert "Revert "Add ActiveIssue attr to the FileSystem tests"" This reverts commit eb2f9548b08c114b359fab8d867ba50de098fe48. The fix is not present in 2.0.23 * Revert "Revert "Add ActiveIssue to the MemoryMappedFiles tests"" This reverts commit 8be39f583499a8d8451034c65260a785330b0795. The fix is not present in 2.0.23 * Increase timeout for AOT tests * Add description of emscripten bump to README * Try to get information about resources * Get all limits * Escape & chars * Reduce platform matrix * Lets try one more build with doubled timeout * Revert "Lets try one more build with doubled timeout" This reverts commit 67dd7754bb79218b2c6b687034162d041715093e. * Try -Wl,-O0 on CI To be sure it behaves the same as in local build * Use -Wl,-lto-O0 do lower link time optimization It looks like it reduces the memory load a lot * Set EmccLinkOptimizationFlag for AOT tests And reset the default value * Escape commas * Revert "Reduce platform matrix" This reverts commit fec0e557208eb165824e75cd57b895a74d164de4. * Remove resource info retrieval * Bump emsdk versions Co-authored-by: Larry Ewing --- .../libraries/webassembly-instructions.md | 2 +- eng/Version.Details.xml | 4 ++-- eng/Versions.props | 4 ++-- eng/pipelines/common/platform-matrix.yml | 2 +- .../libraries/helix-queues-setup.yml | 2 +- eng/testing/tests.wasm.targets | 2 +- src/libraries/sendtohelixhelp.proj | 1 + .../WorkloadManifest.json.in | 22 +++++++++---------- src/mono/wasm/README.md | 13 ++++++++++- src/mono/wasm/emscripten-version.txt | 2 +- 10 files changed, 33 insertions(+), 21 deletions(-) diff --git a/docs/workflow/building/libraries/webassembly-instructions.md b/docs/workflow/building/libraries/webassembly-instructions.md index 80af1a508b106..fdc5dc31d2226 100644 --- a/docs/workflow/building/libraries/webassembly-instructions.md +++ b/docs/workflow/building/libraries/webassembly-instructions.md @@ -7,7 +7,7 @@ If you haven't already done so, please read [this document](../../README.md#Buil 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.21`. See [emscripten-version.txt](..\..\..\..\src\mono\wasm\emscripten-version.txt) +Do not install `latest` but rather specific version e.g. `./emsdk install 2.0.23`. See [emscripten-version.txt](..\..\..\..\src\mono\wasm\emscripten-version.txt) Once installed the `EMSDK_PATH` environment variable needs to be set: diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 5c1f457ae40f4..9c44cabfca804 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -207,9 +207,9 @@ https://dev.azure.com/dnceng/internal/_git/dotnet-optimization 4e5bea15eb5a9c8cf9142195b1c9c78437a5b27f - + https://github.com/dotnet/emsdk - defa37b05c734e025292c5747664e970cd2ac444 + 617928847d1e11458527b8bbafb5577982291847 https://github.com/dotnet/hotreload-utils diff --git a/eng/Versions.props b/eng/Versions.props index a194baa33def9..3b887fae24aa2 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -176,7 +176,7 @@ 11.1.0-alpha.1.21308.1 11.1.0-alpha.1.21308.1 - 6.0.0-preview.6.21275.1 - $(MicrosoftNETRuntimeEmscripten2021Nodewinx64Version) + 6.0.0-preview.7.21323.1 + $(MicrosoftNETRuntimeEmscripten2023Nodewinx64Version) diff --git a/eng/pipelines/common/platform-matrix.yml b/eng/pipelines/common/platform-matrix.yml index daedd73b0e4bf..defd34be84ae2 100644 --- a/eng/pipelines/common/platform-matrix.yml +++ b/eng/pipelines/common/platform-matrix.yml @@ -219,7 +219,7 @@ jobs: targetRid: browser-wasm platform: Browser_wasm container: - image: ubuntu-18.04-webassembly-20210519131124-ba00c14 + image: ubuntu-18.04-webassembly-20210531091624-f5c7a43 registry: mcr jobParameters: runtimeFlavor: ${{ parameters.runtimeFlavor }} diff --git a/eng/pipelines/libraries/helix-queues-setup.yml b/eng/pipelines/libraries/helix-queues-setup.yml index b766f1af96aec..7d33d26edb012 100644 --- a/eng/pipelines/libraries/helix-queues-setup.yml +++ b/eng/pipelines/libraries/helix-queues-setup.yml @@ -189,6 +189,6 @@ jobs: # WebAssembly windows - ${{ if eq(parameters.platform, 'Browser_wasm_win') }}: - - (Windows.Server.Core.1909.Amd64.Open)windows.10.amd64.server20h1.open@mcr.microsoft.com/dotnet-buildtools/prereqs:windowsservercore-2004-helix-webassembly-amd64-20210519130955-ba00c14 + - (Windows.Server.Core.1909.Amd64.Open)windows.10.amd64.server20h1.open@mcr.microsoft.com/dotnet-buildtools/prereqs:windowsservercore-2004-helix-webassembly-amd64-20210531091615-f5c7a43 ${{ insert }}: ${{ parameters.jobParameters }} diff --git a/eng/testing/tests.wasm.targets b/eng/testing/tests.wasm.targets index de4432bee9f5e..aa4fb305dbd42 100644 --- a/eng/testing/tests.wasm.targets +++ b/eng/testing/tests.wasm.targets @@ -40,7 +40,7 @@ <_AOTBuildCommand Condition="'$(ContinuousIntegrationBuild)' != 'true'">$(_AOTBuildCommand) /p:RuntimeSrcDir=$(RepoRoot) /p:RuntimeConfig=$(Configuration) - <_AOTBuildCommand>$(_AOTBuildCommand) /p:RunAOTCompilation=$(RunAOTCompilation) + <_AOTBuildCommand>$(_AOTBuildCommand) /p:RunAOTCompilation=$(RunAOTCompilation) /p:EmccLinkOptimizationFlag='-Oz -Wl%252C-O0 -Wl%252C-lto-O0' <_AOTBuildCommand>$(_AOTBuildCommand) && cd wasm_build/AppBundle $(_AOTBuildCommand) diff --git a/src/libraries/sendtohelixhelp.proj b/src/libraries/sendtohelixhelp.proj index 25d7168bdc463..12d402fcb7e39 100644 --- a/src/libraries/sendtohelixhelp.proj +++ b/src/libraries/sendtohelixhelp.proj @@ -29,6 +29,7 @@ <_workItemTimeout Condition="'$(Scenario)' == '' and '$(_workItemTimeout)' == '' and ('$(TargetArchitecture)' == 'arm64' or '$(TargetArchitecture)' == 'arm')">00:45:00 <_workItemTimeout Condition="'$(Scenario)' != '' and '$(_workItemTimeout)' == '' and ('$(TargetArchitecture)' == 'arm64' or '$(TargetArchitecture)' == 'arm')">01:00:00 <_workItemTimeout Condition="'$(Scenario)' == 'BuildWasmApps' and '$(_workItemTimeout)' == ''">01:00:00 + <_workItemTimeout Condition="'$(TargetOS)' == 'Browser' and '$(NeedsToBuildWasmAppsOnHelix)' == 'true'">01:00:00 <_workItemTimeout Condition="'$(Scenario)' == '' and '$(_workItemTimeout)' == '' and '$(Outerloop)' == 'true'">00:20:00 <_workItemTimeout Condition="'$(Scenario)' == '' and '$(_workItemTimeout)' == ''">00:15:00 <_workItemTimeout Condition="'$(Scenario)' != '' and '$(_workItemTimeout)' == ''">00:30:00 diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in index d783615a908e1..4c50a08f007c8 100644 --- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in +++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in @@ -270,29 +270,29 @@ "kind": "Sdk", "version": "${EmscriptenVersion}", "alias-to": { - "win-x64": "Microsoft.NET.Runtime.Emscripten.2.0.21.Node.win-x64", - "linux-x64": "Microsoft.NET.Runtime.Emscripten.2.0.21.Node.linux-x64", - "osx-x64": "Microsoft.NET.Runtime.Emscripten.2.0.21.Node.osx-x64", - "osx-arm64": "Microsoft.NET.Runtime.Emscripten.2.0.21.Node.osx-x64" + "win-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Node.win-x64", + "linux-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Node.linux-x64", + "osx-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Node.osx-x64", + "osx-arm64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Node.osx-x64" } }, "Microsoft.NET.Runtime.Emscripten.Python" : { "kind": "Sdk", "version": "${EmscriptenVersion}", "alias-to": { - "win-x64": "Microsoft.NET.Runtime.Emscripten.2.0.21.Python.win-x64", - "osx-x64": "Microsoft.NET.Runtime.Emscripten.2.0.21.Python.osx-x64", - "osx-arm64": "Microsoft.NET.Runtime.Emscripten.2.0.21.Python.osx-x64" + "win-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Python.win-x64", + "osx-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Python.osx-x64", + "osx-arm64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Python.osx-x64" } }, "Microsoft.NET.Runtime.Emscripten.Sdk" : { "kind": "Sdk", "version": "${EmscriptenVersion}", "alias-to": { - "win-x64": "Microsoft.NET.Runtime.Emscripten.2.0.21.Sdk.win-x64", - "linux-x64": "Microsoft.NET.Runtime.Emscripten.2.0.21.Sdk.linux-x64", - "osx-x64": "Microsoft.NET.Runtime.Emscripten.2.0.21.Sdk.osx-x64", - "osx-arm64": "Microsoft.NET.Runtime.Emscripten.2.0.21.Sdk.osx-x64" + "win-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Sdk.win-x64", + "linux-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Sdk.linux-x64", + "osx-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Sdk.osx-x64", + "osx-arm64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Sdk.osx-x64" } } } diff --git a/src/mono/wasm/README.md b/src/mono/wasm/README.md index a44edd53dc3dd..67cf64b8c0576 100644 --- a/src/mono/wasm/README.md +++ b/src/mono/wasm/README.md @@ -13,7 +13,7 @@ Note: `EMSDK_PATH` is set by default in `src/mono/wasm/Makefile`, so building ta you are directly using the `dotnet build`, or `build.sh`. * Alternatively you can install **correct version** yourself from the [Emscripten SDK guide](https://emscripten.org/docs/getting_started/downloads.html). -Do not install `latest` but rather specific version e.g. `./emsdk install 2.0.21`. See [emscripten-version.txt](./emscripten-version.txt) +Do not install `latest` but rather specific version e.g. `./emsdk install 2.0.23`. See [emscripten-version.txt](./emscripten-version.txt) Make sure to set `EMSDK_PATH` variable, whenever building, or running tests for wasm. @@ -154,3 +154,14 @@ The samples in `src/mono/sample/wasm` can be build and run like this: `dotnet build /t:RunSample browser/Wasm.Browser.Sample.csproj` To build and run the samples with AOT, add `/p:RunAOTCompilation=true` to the above command lines. + +### Upgrading Emscripten + +Bumping Emscripten version involves these steps: + +* update https://github.com/dotnet/runtime/blob/main/src/mono/wasm/emscripten-version.txt +* bump emscripten versions in docker images in https://github.com/dotnet/dotnet-buildtools-prereqs-docker +* bump emscripten in https://github.com/dotnet/emsdk +* update version number in docs +* update `Microsoft.NET.Runtime.Emscripten..Node.win-x64` package name, version and sha hash in https://github.com/dotnet/runtime/blob/main/eng/Version.Details.xml and in https://github.com/dotnet/runtime/blob/main/eng/Versions.props. the sha is the commit hash in https://github.com/dotnet/emsdk and the package version can be found at https://dev.azure.com/dnceng/public/_packaging?_a=feed&feed=dotnet6 +* update packages in the workload manifest https://github.com/dotnet/runtime/blob/main/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in diff --git a/src/mono/wasm/emscripten-version.txt b/src/mono/wasm/emscripten-version.txt index eb4d1bc7a34e7..8fe1046e01fc8 100644 --- a/src/mono/wasm/emscripten-version.txt +++ b/src/mono/wasm/emscripten-version.txt @@ -1 +1 @@ -2.0.21 \ No newline at end of file +2.0.23 \ No newline at end of file From 74322d7c4d2d5db16825bb8500c757c91f0fecb9 Mon Sep 17 00:00:00 2001 From: monojenkins Date: Thu, 24 Jun 2021 11:33:14 -0400 Subject: [PATCH 105/107] Fix for heap_use_after_free flagged by sanitizer (#54679) Copy of https://github.com/mono/mono/pull/21120 Co-authored-by: dseshadri --- src/mono/mono/mini/mini-posix.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/mono/mono/mini/mini-posix.c b/src/mono/mono/mini/mini-posix.c index 6ff3228ca06b3..b273a96a5faa6 100644 --- a/src/mono/mono/mini/mini-posix.c +++ b/src/mono/mono/mini/mini-posix.c @@ -135,18 +135,31 @@ mono_runtime_shutdown_handlers (void) static GHashTable *mono_saved_signal_handlers = NULL; static struct sigaction * -get_saved_signal_handler (int signo, gboolean remove) +get_saved_signal_handler (int signo) { if (mono_saved_signal_handlers) { /* The hash is only modified during startup, so no need for locking */ struct sigaction *handler = (struct sigaction*)g_hash_table_lookup (mono_saved_signal_handlers, GINT_TO_POINTER (signo)); - if (remove && handler) - g_hash_table_remove (mono_saved_signal_handlers, GINT_TO_POINTER (signo)); return handler; } return NULL; } + +static void +remove_saved_signal_handler (int signo) +{ + if (mono_saved_signal_handlers) { + /* The hash is only modified during startup, so no need for locking */ + struct sigaction *handler = (struct sigaction*)g_hash_table_lookup (mono_saved_signal_handlers, GINT_TO_POINTER (signo)); + if (handler) + g_hash_table_remove (mono_saved_signal_handlers, GINT_TO_POINTER (signo)); + } + return; +} + + + static void save_old_signal_handler (int signo, struct sigaction *old_action) { @@ -181,7 +194,7 @@ gboolean MONO_SIG_HANDLER_SIGNATURE (mono_chain_signal) { int signal = MONO_SIG_HANDLER_GET_SIGNO (); - struct sigaction *saved_handler = (struct sigaction *)get_saved_signal_handler (signal, FALSE); + struct sigaction *saved_handler = (struct sigaction *)get_saved_signal_handler (signal); if (saved_handler && saved_handler->sa_handler) { if (!(saved_handler->sa_flags & SA_SIGINFO)) { @@ -376,7 +389,7 @@ static void remove_signal_handler (int signo) { struct sigaction sa; - struct sigaction *saved_action = get_saved_signal_handler (signo, TRUE); + struct sigaction *saved_action = get_saved_signal_handler (signo); if (!saved_action) { sa.sa_handler = SIG_DFL; @@ -387,6 +400,7 @@ remove_signal_handler (int signo) } else { g_assert (sigaction (signo, saved_action, NULL) != -1); } + remove_saved_signal_handler(signo); } void From ea1707cf3a4a513de37a41d89591cca88460e9fa Mon Sep 17 00:00:00 2001 From: Daniel Genkin Date: Thu, 24 Jun 2021 11:39:04 -0400 Subject: [PATCH 106/107] [WASM] Fix async/await in config loading (#54652) * Fixed config issue * Updated Hot Reload test --- src/mono/sample/mbr/browser/runtime.js | 2 +- src/mono/sample/wasm/browser-bench/runtime.js | 2 +- src/mono/sample/wasm/browser-profile/runtime.js | 2 +- src/mono/sample/wasm/browser/runtime.js | 2 +- .../wasm/debugger/tests/debugger-test/runtime-debugger.js | 2 +- src/mono/wasm/runtime-test.js | 2 +- src/mono/wasm/runtime/library_mono.js | 4 ++-- src/tests/FunctionalTests/WebAssembly/Browser/AOT/runtime.js | 2 +- .../FunctionalTests/WebAssembly/Browser/HotReload/runtime.js | 2 +- .../WebAssembly/Browser/NormalInterp/runtime.js | 2 +- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/mono/sample/mbr/browser/runtime.js b/src/mono/sample/mbr/browser/runtime.js index 32918aa573a44..74781179a31f8 100644 --- a/src/mono/sample/mbr/browser/runtime.js +++ b/src/mono/sample/mbr/browser/runtime.js @@ -5,7 +5,7 @@ var Module = { config: null, preInit: async function() { - Module.config = await MONO.mono_wasm_load_config("./mono-config.json"); + await MONO.mono_wasm_load_config("./mono-config.json"); // sets Module.config implicitly }, // Called when the runtime is initialized and wasm is ready diff --git a/src/mono/sample/wasm/browser-bench/runtime.js b/src/mono/sample/wasm/browser-bench/runtime.js index 60c383d13cb52..f9bdf8b3af7ab 100644 --- a/src/mono/sample/wasm/browser-bench/runtime.js +++ b/src/mono/sample/wasm/browser-bench/runtime.js @@ -4,7 +4,7 @@ var Module = { config: null, preInit: async function() { - Module.config = await MONO.mono_wasm_load_config("./mono-config.json"); + await MONO.mono_wasm_load_config("./mono-config.json"); // sets Module.config implicitly }, // Called when the runtime is initialized and wasm is ready diff --git a/src/mono/sample/wasm/browser-profile/runtime.js b/src/mono/sample/wasm/browser-profile/runtime.js index 2c83ff54ef93d..f0636985d0358 100644 --- a/src/mono/sample/wasm/browser-profile/runtime.js +++ b/src/mono/sample/wasm/browser-profile/runtime.js @@ -5,7 +5,7 @@ var Module = { config: null, preInit: async function() { - Module.config = await MONO.mono_wasm_load_config("./mono-config.json"); + await MONO.mono_wasm_load_config("./mono-config.json"); // sets Module.config implicitly }, // Called when the runtime is initialized and wasm is ready diff --git a/src/mono/sample/wasm/browser/runtime.js b/src/mono/sample/wasm/browser/runtime.js index e97feef745a95..816136acf36b6 100644 --- a/src/mono/sample/wasm/browser/runtime.js +++ b/src/mono/sample/wasm/browser/runtime.js @@ -6,7 +6,7 @@ var Module = { config: null, preInit: async function() { - Module.config = await MONO.mono_wasm_load_config("./mono-config.json"); + await MONO.mono_wasm_load_config("./mono-config.json"); // sets Module.config implicitly }, // Called when the runtime is initialized and wasm is ready diff --git a/src/mono/wasm/debugger/tests/debugger-test/runtime-debugger.js b/src/mono/wasm/debugger/tests/debugger-test/runtime-debugger.js index 360aa9b8b3036..8fb1e86211afe 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/runtime-debugger.js +++ b/src/mono/wasm/debugger/tests/debugger-test/runtime-debugger.js @@ -5,7 +5,7 @@ var Module = { config: null, preInit: async function() { - Module.config = await MONO.mono_wasm_load_config("./mono-config.json"); + await MONO.mono_wasm_load_config("./mono-config.json"); // sets Module.config implicitly }, // Called when the runtime is initialized and wasm is ready diff --git a/src/mono/wasm/runtime-test.js b/src/mono/wasm/runtime-test.js index 8b3395f4ec50e..89b9c5d70cc7e 100644 --- a/src/mono/wasm/runtime-test.js +++ b/src/mono/wasm/runtime-test.js @@ -211,7 +211,7 @@ var Module = { printErr, preInit: async function() { - Module.config = await MONO.mono_wasm_load_config("./mono-config.json"); + await MONO.mono_wasm_load_config("./mono-config.json"); // sets Module.config implicitly }, onAbort: function(x) { diff --git a/src/mono/wasm/runtime/library_mono.js b/src/mono/wasm/runtime/library_mono.js index 84fb5684abd80..8f8d4cbcfe537 100644 --- a/src/mono/wasm/runtime/library_mono.js +++ b/src/mono/wasm/runtime/library_mono.js @@ -1451,9 +1451,9 @@ var MonoSupportLib = { } else { // shell or worker config = JSON.parse(read(configFilePath)); // read is a v8 debugger command } - return config; + Module.config = config; } catch(e) { - return {message: "failed to load config file", error: e}; + Module.config = {message: "failed to load config file", error: e}; } finally { Module.removeRunDependency(configFilePath); } diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/AOT/runtime.js b/src/tests/FunctionalTests/WebAssembly/Browser/AOT/runtime.js index 65cba13a9b126..11f8d64f60c51 100644 --- a/src/tests/FunctionalTests/WebAssembly/Browser/AOT/runtime.js +++ b/src/tests/FunctionalTests/WebAssembly/Browser/AOT/runtime.js @@ -6,7 +6,7 @@ var Module = { config: null, preInit: async function() { - Module.config = await MONO.mono_wasm_load_config("./mono-config.json"); + await MONO.mono_wasm_load_config("./mono-config.json"); // sets Module.config implicitly }, onRuntimeInitialized: function () { diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/runtime.js b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/runtime.js index 4859991a626f4..94f1a36a41073 100644 --- a/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/runtime.js +++ b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/runtime.js @@ -6,7 +6,7 @@ var Module = { config: null, preInit: async function() { - Module.config = await MONO.mono_wasm_load_config("./mono-config.json"); + await MONO.mono_wasm_load_config("./mono-config.json"); // sets Module.config implicitly }, onRuntimeInitialized: function () { diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/runtime.js b/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/runtime.js index 1a8abf503fb33..b5227472674c6 100644 --- a/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/runtime.js +++ b/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/runtime.js @@ -6,7 +6,7 @@ var Module = { config: null, preInit: async function() { - Module.config = await MONO.mono_wasm_load_config("./mono-config.json"); + await MONO.mono_wasm_load_config("./mono-config.json"); // sets Module.config implicitly }, onRuntimeInitialized: function () { From abccfadbf570033efee8ac9a6992f6f7ee51f474 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Thu, 24 Jun 2021 17:43:55 +0200 Subject: [PATCH 107/107] Disable MacCatalyst arm64 PR test runs on staging pipeline (#54678) We don't have enough capacity right now on Helix to handle the load. --- eng/pipelines/runtime-staging.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/eng/pipelines/runtime-staging.yml b/eng/pipelines/runtime-staging.yml index 2352995df910a..666641354bf5a 100644 --- a/eng/pipelines/runtime-staging.yml +++ b/eng/pipelines/runtime-staging.yml @@ -107,7 +107,9 @@ jobs: runtimeFlavor: mono platforms: - MacCatalyst_x64 - - MacCatalyst_arm64 + # don't run tests on MacCatalyst_arm64 PRs until we can get significantly more devices + - ${{ if eq(variables['isFullMatrix'], true) }}: + - MacCatalyst_arm64 variables: # map dependencies variables to local variables - name: librariesContainsChange