From b66a11455c7e1154002ef431d26c6d21ed227a57 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Fri, 8 Oct 2021 08:06:30 +0200 Subject: [PATCH 001/106] Support passing total code size through when doing SPMI diffs (#60124) Currently the total code bytes shown for the base/diff is misleading when SPMI is used because SPMI generates only .dasm files for method contexts with diffs. This change: * Adds -baseMetricsSummary and -diffMetricsSummary to SPMI, which specifies a file path to output metrics. Currently that's just the number of failing/succeeding compiles, and the total number of code bytes, but the hope is to use this for other metrics as well. * Adds --override-total-base-metric and --override-total-diff-metric to jit-analyze, to support overriding the computed total base and total diff metric, since they are misleading when .dasm files are only created for differences * Supports this from the superpmi.py wrapper script for asmdiffs when no explicit metric is provided: in this case, the script will get the metrics from SPMI and pass them to jit-analyze to override the computed totals. The net result is that the displayed results from jit-analyze after SPMI runs are less misleading and should be more comparable to similar PMI runs. --- .../superpmi/superpmi-shared/standardpch.h | 2 + .../ToolBox/superpmi/superpmi/CMakeLists.txt | 1 + .../ToolBox/superpmi/superpmi/commandline.cpp | 30 +++ .../ToolBox/superpmi/superpmi/commandline.h | 4 + .../ToolBox/superpmi/superpmi/jitinstance.cpp | 10 +- .../ToolBox/superpmi/superpmi/jitinstance.h | 2 +- .../superpmi/superpmi/methodstatsemitter.h | 1 + .../superpmi/superpmi/metricssummary.cpp | 90 +++++++++ .../superpmi/superpmi/metricssummary.h | 26 +++ .../superpmi/superpmi/parallelsuperpmi.cpp | 189 +++++++++++++----- .../ToolBox/superpmi/superpmi/superpmi.cpp | 18 +- src/coreclr/inc/clr_std/vector | 5 + src/coreclr/scripts/superpmi.py | 48 ++++- 13 files changed, 371 insertions(+), 55 deletions(-) create mode 100644 src/coreclr/ToolBox/superpmi/superpmi/metricssummary.cpp create mode 100644 src/coreclr/ToolBox/superpmi/superpmi/metricssummary.h diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shared/standardpch.h b/src/coreclr/ToolBox/superpmi/superpmi-shared/standardpch.h index 731e8305da695..1dfae365bc4db 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shared/standardpch.h +++ b/src/coreclr/ToolBox/superpmi/superpmi-shared/standardpch.h @@ -68,12 +68,14 @@ #ifdef TARGET_UNIX #include "clr_std/string" #include "clr_std/algorithm" +#include "clr_std/vector" #else // !TARGET_UNIX #ifndef USE_STL #define USE_STL #endif // USE_STL #include #include +#include #endif // !TARGET_UNIX #ifdef USE_MSVCDIS diff --git a/src/coreclr/ToolBox/superpmi/superpmi/CMakeLists.txt b/src/coreclr/ToolBox/superpmi/superpmi/CMakeLists.txt index 92cda6d5600fe..4827c195dc299 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi/CMakeLists.txt +++ b/src/coreclr/ToolBox/superpmi/superpmi/CMakeLists.txt @@ -22,6 +22,7 @@ set(SUPERPMI_SOURCES jitdebugger.cpp jitinstance.cpp methodstatsemitter.cpp + metricssummary.cpp neardiffer.cpp parallelsuperpmi.cpp superpmi.cpp diff --git a/src/coreclr/ToolBox/superpmi/superpmi/commandline.cpp b/src/coreclr/ToolBox/superpmi/superpmi/commandline.cpp index e4d98a13752d6..c4735b23d7527 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi/commandline.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi/commandline.cpp @@ -81,6 +81,16 @@ void CommandLine::DumpHelp(const char* program) printf(" t - method throughput time\n"); printf(" * - all available method stats\n"); printf("\n"); + printf(" -metricsSummary , -baseMetricsSummary \n"); + printf(" Emit a summary of metrics to the specified file\n"); + printf(" Currently includes:\n"); + printf(" Total number of successful SPMI compiles\n"); + printf(" Total number of failing SPMI compiles\n"); + printf(" Total amount of ASM code in bytes\n"); + printf("\n"); + printf(" -diffMetricsSummary \n"); + printf(" Same as above, but emit for the diff/second JIT"); + printf("\n"); printf(" -a[pplyDiff]\n"); printf(" Compare the compile result generated from the provided JIT with the\n"); printf(" compile result stored with the MC. If two JITs are provided, this\n"); @@ -374,6 +384,26 @@ bool CommandLine::Parse(int argc, char* argv[], /* OUT */ Options* o) o->methodStatsTypes = argv[i]; } + else if ((_strnicmp(&argv[i][1], "metricsSummary", argLen) == 0) || (_strnicmp(&argv[i][1], "baseMetricsSummary", argLen) == 0)) + { + if (++i >= argc) + { + DumpHelp(argv[0]); + return false; + } + + o->baseMetricsSummaryFile = argv[i]; + } + else if ((_strnicmp(&argv[i][1], "diffMetricsSummary", argLen) == 0)) + { + if (++i >= argc) + { + DumpHelp(argv[0]); + return false; + } + + o->diffMetricsSummaryFile = argv[i]; + } else if ((_strnicmp(&argv[i][1], "applyDiff", argLen) == 0)) { o->applyDiff = true; diff --git a/src/coreclr/ToolBox/superpmi/superpmi/commandline.h b/src/coreclr/ToolBox/superpmi/superpmi/commandline.h index e9bae5740262b..055adf00295cd 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi/commandline.h +++ b/src/coreclr/ToolBox/superpmi/superpmi/commandline.h @@ -36,6 +36,8 @@ class CommandLine , indexes(nullptr) , hash(nullptr) , methodStatsTypes(nullptr) + , baseMetricsSummaryFile(nullptr) + , diffMetricsSummaryFile(nullptr) , mclFilename(nullptr) , diffMCLFilename(nullptr) , targetArchitecture(nullptr) @@ -67,6 +69,8 @@ class CommandLine int* indexes; char* hash; char* methodStatsTypes; + char* baseMetricsSummaryFile; + char* diffMetricsSummaryFile; char* mclFilename; char* diffMCLFilename; char* targetArchitecture; diff --git a/src/coreclr/ToolBox/superpmi/superpmi/jitinstance.cpp b/src/coreclr/ToolBox/superpmi/superpmi/jitinstance.cpp index fafc367669d47..96484d4b86bcd 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi/jitinstance.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi/jitinstance.cpp @@ -8,6 +8,7 @@ #include "jithost.h" #include "errorhandling.h" #include "spmiutil.h" +#include "metricssummary.h" JitInstance* JitInstance::InitJit(char* nameOfJit, bool breakOnAssert, @@ -276,7 +277,7 @@ bool JitInstance::reLoad(MethodContext* firstContext) return true; } -JitInstance::Result JitInstance::CompileMethod(MethodContext* MethodToCompile, int mcIndex, bool collectThroughput) +JitInstance::Result JitInstance::CompileMethod(MethodContext* MethodToCompile, int mcIndex, bool collectThroughput, MetricsSummary* metrics) { struct Param : FilterSuperPMIExceptionsParam_CaptureException { @@ -286,12 +287,14 @@ JitInstance::Result JitInstance::CompileMethod(MethodContext* MethodToCompile, i unsigned flags; int mcIndex; bool collectThroughput; + MetricsSummary* metrics; } param; param.pThis = this; param.result = RESULT_SUCCESS; // assume success param.flags = 0; param.mcIndex = mcIndex; param.collectThroughput = collectThroughput; + param.metrics = metrics; // store to instance field our raw values, so we can figure things out a bit later... mc = MethodToCompile; @@ -365,11 +368,16 @@ JitInstance::Result JitInstance::CompileMethod(MethodContext* MethodToCompile, i pParam->pThis->mc->cr->recAllocGCInfoCapture(); pParam->pThis->mc->cr->recMessageLog("Successful Compile"); + + pParam->metrics->SuccessfulCompiles++; + pParam->metrics->NumCodeBytes += NCodeSizeBlock; } else { LogDebug("compileMethod failed with result %d", jitResult); pParam->result = RESULT_ERROR; + + pParam->metrics->FailingCompiles++; } } PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CaptureExceptionAndStop) diff --git a/src/coreclr/ToolBox/superpmi/superpmi/jitinstance.h b/src/coreclr/ToolBox/superpmi/superpmi/jitinstance.h index c572008481a65..8e8fbabf82ead 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi/jitinstance.h +++ b/src/coreclr/ToolBox/superpmi/superpmi/jitinstance.h @@ -60,7 +60,7 @@ class JitInstance bool resetConfig(MethodContext* firstContext); - Result CompileMethod(MethodContext* MethodToCompile, int mcIndex, bool collectThroughput); + Result CompileMethod(MethodContext* MethodToCompile, int mcIndex, bool collectThroughput, class MetricsSummary* summary); const WCHAR* getForceOption(const WCHAR* key); const WCHAR* getOption(const WCHAR* key); diff --git a/src/coreclr/ToolBox/superpmi/superpmi/methodstatsemitter.h b/src/coreclr/ToolBox/superpmi/superpmi/methodstatsemitter.h index 083d24fe852a9..40d38698b3d44 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi/methodstatsemitter.h +++ b/src/coreclr/ToolBox/superpmi/superpmi/methodstatsemitter.h @@ -24,4 +24,5 @@ class MethodStatsEmitter void Emit(int methodNumber, MethodContext* mc, ULONGLONG firstTime, ULONGLONG secondTime); void SetStatsTypes(char* types); }; + #endif diff --git a/src/coreclr/ToolBox/superpmi/superpmi/metricssummary.cpp b/src/coreclr/ToolBox/superpmi/superpmi/metricssummary.cpp new file mode 100644 index 0000000000000..e7c725d7845bf --- /dev/null +++ b/src/coreclr/ToolBox/superpmi/superpmi/metricssummary.cpp @@ -0,0 +1,90 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "standardpch.h" +#include "metricssummary.h" +#include "logging.h" + +struct HandleCloser +{ + void operator()(HANDLE hFile) + { + CloseHandle(hFile); + } +}; + +struct FileHandleWrapper +{ + FileHandleWrapper(HANDLE hFile) + : hFile(hFile) + { + } + + ~FileHandleWrapper() + { + CloseHandle(hFile); + } + + HANDLE get() { return hFile; } + +private: + HANDLE hFile; +}; + +bool MetricsSummary::SaveToFile(const char* path) +{ + FileHandleWrapper file(CreateFile(path, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr)); + if (file.get() == INVALID_HANDLE_VALUE) + { + return false; + } + + char buffer[4096]; + int len = + sprintf_s(buffer, sizeof(buffer), "Successful compiles,Failing compiles,Code bytes\n%d,%d,%lld\n", + SuccessfulCompiles, FailingCompiles, NumCodeBytes); + DWORD numWritten; + if (!WriteFile(file.get(), buffer, static_cast(len), &numWritten, nullptr) || numWritten != static_cast(len)) + { + return false; + } + + return true; +} + +bool MetricsSummary::LoadFromFile(const char* path, MetricsSummary* metrics) +{ + FileHandleWrapper file(CreateFile(path, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr)); + if (file.get() == INVALID_HANDLE_VALUE) + { + return false; + } + + LARGE_INTEGER len; + if (!GetFileSizeEx(file.get(), &len)) + { + return false; + } + + std::vector content(static_cast(len.QuadPart)); + DWORD numRead; + if (!ReadFile(file.get(), content.data(), static_cast(content.size()), &numRead, nullptr) || numRead != content.size()) + { + return false; + } + + if (sscanf_s(content.data(), "Successful compiles,Failing compiles,Code bytes\n%d,%d,%lld\n", + &metrics->SuccessfulCompiles, &metrics->FailingCompiles, &metrics->NumCodeBytes) != 3) + { + return false; + } + + return true; +} + +void MetricsSummary::AggregateFrom(const MetricsSummary& other) +{ + SuccessfulCompiles += other.SuccessfulCompiles; + FailingCompiles += other.FailingCompiles; + NumCodeBytes += other.NumCodeBytes; +} diff --git a/src/coreclr/ToolBox/superpmi/superpmi/metricssummary.h b/src/coreclr/ToolBox/superpmi/superpmi/metricssummary.h new file mode 100644 index 0000000000000..4f7d825c2d43a --- /dev/null +++ b/src/coreclr/ToolBox/superpmi/superpmi/metricssummary.h @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef _MetricsSummary +#define _MetricsSummary + +class MetricsSummary +{ +public: + int SuccessfulCompiles; + int FailingCompiles; + long long NumCodeBytes; + + MetricsSummary() + : SuccessfulCompiles(0) + , FailingCompiles(0) + , NumCodeBytes(0) + { + } + + bool SaveToFile(const char* path); + static bool LoadFromFile(const char* path, MetricsSummary* metrics); + void AggregateFrom(const MetricsSummary& other); +}; + +#endif diff --git a/src/coreclr/ToolBox/superpmi/superpmi/parallelsuperpmi.cpp b/src/coreclr/ToolBox/superpmi/superpmi/parallelsuperpmi.cpp index 46c1a7a91132d..92b172e196d1c 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi/parallelsuperpmi.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi/parallelsuperpmi.cpp @@ -8,6 +8,7 @@ #include "lightweightmap.h" #include "commandline.h" #include "errorhandling.h" +#include "metricssummary.h" #define MAX_LOG_LINE_SIZE 0x1000 // 4 KB @@ -293,6 +294,35 @@ void ProcessChildStdOut(const CommandLine::Options& o, } } +static bool ProcessChildMetrics(const char* baseMetricsSummaryPath, const char* diffMetricsSummaryPath, MetricsSummary* baseMetrics, MetricsSummary* diffMetrics) +{ + if (baseMetricsSummaryPath != nullptr) + { + MetricsSummary childBaseMetrics; + if (!MetricsSummary::LoadFromFile(baseMetricsSummaryPath, &childBaseMetrics)) + { + LogError("Couldn't load base metrics summary created by child process"); + return false; + } + + baseMetrics->AggregateFrom(childBaseMetrics); + } + + if (diffMetricsSummaryPath != nullptr) + { + MetricsSummary childDiffMetrics; + if (!MetricsSummary::LoadFromFile(diffMetricsSummaryPath, &childDiffMetrics)) + { + LogError("Couldn't load diff metrics summary created by child process"); + return false; + } + + diffMetrics->AggregateFrom(childDiffMetrics); + } + + return true; +} + #ifndef TARGET_UNIX // TODO-Porting: handle Ctrl-C signals gracefully on Unix BOOL WINAPI CtrlHandler(DWORD fdwCtrlType) { @@ -309,15 +339,39 @@ int __cdecl compareInt(const void* arg1, const void* arg2) return (*(const int*)arg1) - (*(const int*)arg2); } -// 'arrWorkerMCLPath' is an array of strings of size 'workerCount'. -void MergeWorkerMCLs(char* mclFilename, char** arrWorkerMCLPath, int workerCount) +struct PerWorkerData +{ + HANDLE hStdOutput; + HANDLE hStdError; + + char* failingMCListPath; + char* diffMCListPath; + char* stdOutputPath; + char* stdErrorPath; + char* baseMetricsSummaryPath; + char* diffMetricsSummaryPath; + + PerWorkerData() + : hStdOutput(INVALID_HANDLE_VALUE) + , hStdError(INVALID_HANDLE_VALUE) + , failingMCListPath(nullptr) + , diffMCListPath(nullptr) + , stdOutputPath(nullptr) + , stdErrorPath(nullptr) + , baseMetricsSummaryPath(nullptr) + , diffMetricsSummaryPath(nullptr) + { + } +}; + +void MergeWorkerMCLs(char* mclFilename, PerWorkerData* workerData, int workerCount, char* PerWorkerData::*mclPath) { int **MCL = new int *[workerCount], *MCLCount = new int[workerCount], totalCount = 0; for (int i = 0; i < workerCount; i++) { // Read the next partial MCL file - ReadMCLToArray(arrWorkerMCLPath[i], &MCL[i], &MCLCount[i]); + ReadMCLToArray(workerData[i].*mclPath, &MCL[i], &MCLCount[i]); totalCount += MCLCount[i]; } @@ -474,14 +528,7 @@ int doParallelSuperPMI(CommandLine::Options& o) LogVerbose(" diffMCLFilename=%s", o.diffMCLFilename); LogVerbose(" workerCount=%d, skipCleanup=%d.", o.workerCount, o.skipCleanup); - HANDLE* hProcesses = new HANDLE[o.workerCount]; - HANDLE* hStdOutput = new HANDLE[o.workerCount]; - HANDLE* hStdError = new HANDLE[o.workerCount]; - - char** arrFailingMCListPath = new char*[o.workerCount]; - char** arrDiffMCListPath = new char*[o.workerCount]; - char** arrStdOutputPath = new char*[o.workerCount]; - char** arrStdErrorPath = new char*[o.workerCount]; + PerWorkerData* perWorkerData = new PerWorkerData[o.workerCount]; // Add a random number to the temporary file names to allow multiple parallel SuperPMI to happen at once. unsigned int randNumber = 0; @@ -493,51 +540,71 @@ int doParallelSuperPMI(CommandLine::Options& o) for (int i = 0; i < o.workerCount; i++) { + PerWorkerData& wd = perWorkerData[i]; if (o.mclFilename != nullptr) { - arrFailingMCListPath[i] = new char[MAX_PATH]; - sprintf_s(arrFailingMCListPath[i], MAX_PATH, "%sParallelSuperPMI-%u-%d.mcl", tempPath, randNumber, i); + wd.failingMCListPath = new char[MAX_PATH]; + sprintf_s(wd.failingMCListPath, MAX_PATH, "%sParallelSuperPMI-%u-%d.mcl", tempPath, randNumber, i); } - else + + if (o.diffMCLFilename != nullptr) { - arrFailingMCListPath[i] = nullptr; + wd.diffMCListPath = new char[MAX_PATH]; + sprintf_s(wd.diffMCListPath, MAX_PATH, "%sParallelSuperPMI-Diff-%u-%d.mcl", tempPath, randNumber, i); } - if (o.diffMCLFilename != nullptr) + if (o.baseMetricsSummaryFile != nullptr) { - arrDiffMCListPath[i] = new char[MAX_PATH]; - sprintf_s(arrDiffMCListPath[i], MAX_PATH, "%sParallelSuperPMI-Diff-%u-%d.mcl", tempPath, randNumber, i); + wd.baseMetricsSummaryPath = new char[MAX_PATH]; + sprintf_s(wd.baseMetricsSummaryPath, MAX_PATH, "%sParallelSuperPMI-BaseMetricsSummary-%u-%d.txt", tempPath, randNumber, i); } - else + + if (o.diffMetricsSummaryFile != nullptr) { - arrDiffMCListPath[i] = nullptr; + wd.diffMetricsSummaryPath = new char[MAX_PATH]; + sprintf_s(wd.diffMetricsSummaryPath, MAX_PATH, "%sParallelSuperPMI-DiffMetricsSummary-%u-%d.txt", tempPath, randNumber, i); } - arrStdOutputPath[i] = new char[MAX_PATH]; - arrStdErrorPath[i] = new char[MAX_PATH]; + wd.stdOutputPath = new char[MAX_PATH]; + wd.stdErrorPath = new char[MAX_PATH]; - sprintf_s(arrStdOutputPath[i], MAX_PATH, "%sParallelSuperPMI-stdout-%u-%d.txt", tempPath, randNumber, i); - sprintf_s(arrStdErrorPath[i], MAX_PATH, "%sParallelSuperPMI-stderr-%u-%d.txt", tempPath, randNumber, i); + sprintf_s(wd.stdOutputPath, MAX_PATH, "%sParallelSuperPMI-stdout-%u-%d.txt", tempPath, randNumber, i); + sprintf_s(wd.stdErrorPath, MAX_PATH, "%sParallelSuperPMI-stderr-%u-%d.txt", tempPath, randNumber, i); } char cmdLine[MAX_CMDLINE_SIZE]; cmdLine[0] = '\0'; int bytesWritten; + HANDLE* hProcesses = new HANDLE[o.workerCount]; for (int i = 0; i < o.workerCount; i++) { bytesWritten = sprintf_s(cmdLine, MAX_CMDLINE_SIZE, "%s -stride %d %d", spmiFilename, i + 1, o.workerCount); - if (o.mclFilename != nullptr) + PerWorkerData& wd = perWorkerData[i]; + + if (wd.failingMCListPath != nullptr) { bytesWritten += sprintf_s(cmdLine + bytesWritten, MAX_CMDLINE_SIZE - bytesWritten, " -failingMCList %s", - arrFailingMCListPath[i]); + wd.failingMCListPath); } - if (o.diffMCLFilename != nullptr) + if (wd.diffMCListPath != nullptr) { bytesWritten += sprintf_s(cmdLine + bytesWritten, MAX_CMDLINE_SIZE - bytesWritten, " -diffMCList %s", - arrDiffMCListPath[i]); + wd.diffMCListPath); + } + + if (wd.baseMetricsSummaryPath != nullptr) + { + bytesWritten += sprintf_s(cmdLine + bytesWritten, MAX_CMDLINE_SIZE - bytesWritten, " -baseMetricsSummary %s", + wd.baseMetricsSummaryPath); + } + + if (wd.diffMetricsSummaryPath != nullptr) + { + bytesWritten += sprintf_s(cmdLine + bytesWritten, MAX_CMDLINE_SIZE - bytesWritten, " -diffMetricsSummary %s", + wd.diffMetricsSummaryPath); } if (o.failureLimit > 0) @@ -553,26 +620,26 @@ int doParallelSuperPMI(CommandLine::Options& o) sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE; // Let newly created stdout/stderr handles be inherited. - LogDebug("stdout %i=%s", i, arrStdOutputPath[i]); - hStdOutput[i] = CreateFileA(arrStdOutputPath[i], GENERIC_WRITE, FILE_SHARE_READ, &sa, CREATE_ALWAYS, + LogDebug("stdout %i=%s", i, wd.stdOutputPath); + wd.hStdOutput = CreateFileA(wd.stdOutputPath, GENERIC_WRITE, FILE_SHARE_READ, &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (hStdOutput[i] == INVALID_HANDLE_VALUE) + if (wd.hStdOutput == INVALID_HANDLE_VALUE) { - LogError("Unable to open '%s'. GetLastError()=%u", arrStdOutputPath[i], GetLastError()); + LogError("Unable to open '%s'. GetLastError()=%u", wd.stdOutputPath, GetLastError()); return -1; } - LogDebug("stderr %i=%s", i, arrStdErrorPath[i]); - hStdError[i] = CreateFileA(arrStdErrorPath[i], GENERIC_WRITE, FILE_SHARE_READ, &sa, CREATE_ALWAYS, + LogDebug("stderr %i=%s", i, wd.stdErrorPath); + wd.hStdError = CreateFileA(wd.stdErrorPath, GENERIC_WRITE, FILE_SHARE_READ, &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (hStdError[i] == INVALID_HANDLE_VALUE) + if (wd.hStdError == INVALID_HANDLE_VALUE) { - LogError("Unable to open '%s'. GetLastError()=%u", arrStdErrorPath[i], GetLastError()); + LogError("Unable to open '%s'. GetLastError()=%u", wd.stdErrorPath, GetLastError()); return -1; } // Create a SuperPMI worker process and redirect its output to file - if (!StartProcess(cmdLine, hStdOutput[i], hStdError[i], &hProcesses[i])) + if (!StartProcess(cmdLine, wd.hStdOutput, wd.hStdError, &hProcesses[i])) { return -1; } @@ -583,8 +650,8 @@ int doParallelSuperPMI(CommandLine::Options& o) // Close stdout/stderr for (int i = 0; i < o.workerCount; i++) { - CloseHandle(hStdOutput[i]); - CloseHandle(hStdError[i]); + CloseHandle(perWorkerData[i].hStdOutput); + CloseHandle(perWorkerData[i].hStdError); } SpmiResult result = SpmiResult::Success; @@ -626,13 +693,18 @@ int doParallelSuperPMI(CommandLine::Options& o) bool usageError = false; // variable to flag if we hit a usage error in SuperPMI int loaded = 0, jitted = 0, failed = 0, excluded = 0, missing = 0, diffs = 0; + MetricsSummary baseMetrics; + MetricsSummary diffMetrics; // Read the stderr files and log them as errors // Read the stdout files and parse them for counts and log any MISSING or ISSUE errors for (int i = 0; i < o.workerCount; i++) { - ProcessChildStdErr(arrStdErrorPath[i]); - ProcessChildStdOut(o, arrStdOutputPath[i], &loaded, &jitted, &failed, &excluded, &missing, &diffs, &usageError); + PerWorkerData& wd = perWorkerData[i]; + ProcessChildStdErr(wd.stdErrorPath); + ProcessChildStdOut(o, wd.stdOutputPath, &loaded, &jitted, &failed, &excluded, &missing, &diffs, &usageError); + ProcessChildMetrics(wd.baseMetricsSummaryPath, wd.diffMetricsSummaryPath, &baseMetrics, &diffMetrics); + if (usageError) break; } @@ -640,13 +712,23 @@ int doParallelSuperPMI(CommandLine::Options& o) if (o.mclFilename != nullptr && !usageError) { // Concat the resulting .mcl files - MergeWorkerMCLs(o.mclFilename, arrFailingMCListPath, o.workerCount); + MergeWorkerMCLs(o.mclFilename, perWorkerData, o.workerCount, &PerWorkerData::failingMCListPath); } if (o.diffMCLFilename != nullptr && !usageError) { // Concat the resulting diff .mcl files - MergeWorkerMCLs(o.diffMCLFilename, arrDiffMCListPath, o.workerCount); + MergeWorkerMCLs(o.diffMCLFilename, perWorkerData, o.workerCount, &PerWorkerData::diffMCListPath); + } + + if (o.baseMetricsSummaryFile != nullptr && !usageError) + { + baseMetrics.SaveToFile(o.baseMetricsSummaryFile); + } + + if (o.diffMetricsSummaryFile != nullptr && !usageError) + { + diffMetrics.SaveToFile(o.diffMetricsSummaryFile); } if (!usageError) @@ -670,16 +752,25 @@ int doParallelSuperPMI(CommandLine::Options& o) // Delete all temporary files generated for (int i = 0; i < o.workerCount; i++) { - if (arrFailingMCListPath[i] != nullptr) + PerWorkerData& wd = perWorkerData[i]; + if (wd.failingMCListPath != nullptr) + { + DeleteFile(wd.failingMCListPath); + } + if (wd.diffMCListPath != nullptr) + { + DeleteFile(wd.diffMCListPath); + } + if (wd.baseMetricsSummaryPath != nullptr) { - DeleteFile(arrFailingMCListPath[i]); + DeleteFile(wd.baseMetricsSummaryPath); } - if (arrDiffMCListPath[i] != nullptr) + if (wd.diffMetricsSummaryPath != nullptr) { - DeleteFile(arrDiffMCListPath[i]); + DeleteFile(wd.diffMetricsSummaryPath); } - DeleteFile(arrStdOutputPath[i]); - DeleteFile(arrStdErrorPath[i]); + DeleteFile(wd.stdOutputPath); + DeleteFile(wd.stdErrorPath); } } diff --git a/src/coreclr/ToolBox/superpmi/superpmi/superpmi.cpp b/src/coreclr/ToolBox/superpmi/superpmi/superpmi.cpp index 640dfd367a7f3..5410240bdad33 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi/superpmi.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi/superpmi.cpp @@ -18,6 +18,7 @@ #include "mclist.h" #include "methodstatsemitter.h" #include "spmiutil.h" +#include "metricssummary.h" extern int doParallelSuperPMI(CommandLine::Options& o); @@ -264,6 +265,9 @@ int __cdecl main(int argc, char* argv[]) } } + MetricsSummary baseMetrics; + MetricsSummary diffMetrics; + while (true) { MethodContextBuffer mcb = reader->GetNextMethodContext(); @@ -360,7 +364,7 @@ int __cdecl main(int argc, char* argv[]) jittedCount++; st3.Start(); - res = jit->CompileMethod(mc, reader->GetMethodContextIndex(), collectThroughput); + res = jit->CompileMethod(mc, reader->GetMethodContextIndex(), collectThroughput, &baseMetrics); st3.Stop(); LogDebug("Method %d compiled in %fms, result %d", reader->GetMethodContextIndex(), st3.GetMilliseconds(), res); @@ -378,7 +382,7 @@ int __cdecl main(int argc, char* argv[]) mc->cr = new CompileResult(); st4.Start(); - res2 = jit2->CompileMethod(mc, reader->GetMethodContextIndex(), collectThroughput); + res2 = jit2->CompileMethod(mc, reader->GetMethodContextIndex(), collectThroughput, &diffMetrics); st4.Stop(); LogDebug("Method %d compiled by JIT2 in %fms, result %d", reader->GetMethodContextIndex(), st4.GetMilliseconds(), res2); @@ -603,6 +607,16 @@ int __cdecl main(int argc, char* argv[]) st2.Stop(); LogVerbose("Total time: %fms", st2.GetMilliseconds()); + if (o.baseMetricsSummaryFile != nullptr) + { + baseMetrics.SaveToFile(o.baseMetricsSummaryFile); + } + + if (o.diffMetricsSummaryFile != nullptr) + { + diffMetrics.SaveToFile(o.diffMetricsSummaryFile); + } + if (methodStatsEmitter != nullptr) { delete methodStatsEmitter; diff --git a/src/coreclr/inc/clr_std/vector b/src/coreclr/inc/clr_std/vector index 0bed04182a4ee..c10ecf6ba5a92 100644 --- a/src/coreclr/inc/clr_std/vector +++ b/src/coreclr/inc/clr_std/vector @@ -373,6 +373,11 @@ namespace std m_size -= elements; return iterator(m_pelements + (position - begin())); } + + T* data() + { + return m_pelements; + } const T* data() const { diff --git a/src/coreclr/scripts/superpmi.py b/src/coreclr/scripts/superpmi.py index 4b502f0bffa4f..7618c11480ec0 100755 --- a/src/coreclr/scripts/superpmi.py +++ b/src/coreclr/scripts/superpmi.py @@ -18,6 +18,7 @@ import argparse import asyncio +import csv import datetime import locale import logging @@ -800,6 +801,22 @@ def is_url(path): # If it doesn't look like an URL, treat it like a file, possibly a UNC file. return path.lower().startswith("http:") or path.lower().startswith("https:") +def read_csv_metrics(path): + """ Read a metrics summary file produced by superpmi, and return the single row containing the information as a dictionary. + + Args: + path (str) : path to .csv file + + Returns: + A dictionary with each metric + """ + + with open(path) as csv_file: + reader = csv.DictReader(csv_file) + for row in reader: + return row + + return None ################################################################################ # Helper classes @@ -1800,6 +1817,9 @@ def replay_with_asm_diffs(self): fail_mcl_file = os.path.join(temp_location, os.path.basename(mch_file) + "_fail.mcl") diff_mcl_file = os.path.join(temp_location, os.path.basename(mch_file) + "_diff.mcl") + base_metrics_summary_file = os.path.join(temp_location, os.path.basename(mch_file) + "_base_metrics.csv") + diff_metrics_summary_file = os.path.join(temp_location, os.path.basename(mch_file) + "_diff_metrics.csv") + # If the user passed -temp_dir, we skip the SuperPMI replay process, # and rely on what we find from a previous run. if self.coreclr_args.temp_dir is not None: @@ -1810,7 +1830,9 @@ def replay_with_asm_diffs(self): "-v", "ewmi", # display errors, warnings, missing, jit info "-f", fail_mcl_file, # Failing mc List "-diffMCList", diff_mcl_file, # Create all of the diffs in an mcl file - "-r", os.path.join(temp_location, "repro") # Repro name, create .mc repro files + "-r", os.path.join(temp_location, "repro"), # Repro name, create .mc repro files + "-baseMetricsSummary", base_metrics_summary_file, # Create summary of metrics we can use to get total code size impact + "-diffMetricsSummary", diff_metrics_summary_file, ] flags += altjit_asm_diffs_flags flags += base_option_flags @@ -1839,7 +1861,7 @@ def replay_with_asm_diffs(self): return_code = run_and_log(command) print_superpmi_failure_code(return_code, self.coreclr_args) if return_code == 0: - logging.info("Clean SuperPMI replay") + logging.info("Clean SuperPMI diff") else: result = False # Don't report as replay failure asm diffs (return code 2) or missing data (return code 3). @@ -1961,6 +1983,17 @@ async def create_one_artifact(jit_path: str, location: str, flags) -> str: logging.debug(item) logging.debug("") + base_metrics = read_csv_metrics(base_metrics_summary_file) + diff_metrics = read_csv_metrics(diff_metrics_summary_file) + + if base_metrics is not None and "Code bytes" in base_metrics and diff_metrics is not None and "Code bytes" in diff_metrics: + base_bytes = int(base_metrics["Code bytes"]) + diff_bytes = int(diff_metrics["Code bytes"]) + logging.info("Total Code bytes of base: {}".format(base_bytes)) + logging.info("Total Code bytes of diff: {}".format(diff_bytes)) + delta_bytes = diff_bytes - base_bytes + logging.info("Total Code bytes of delta: {} ({:.2%} of base)".format(delta_bytes, delta_bytes / base_bytes)) + try: current_text_diff = text_differences.get_nowait() except: @@ -1985,6 +2018,9 @@ async def create_one_artifact(jit_path: str, location: str, flags) -> str: command = [ jit_analyze_path, "--md", md_summary_file, "-r", "--base", base_asm_location, "--diff", diff_asm_location ] if self.coreclr_args.metrics: command += [ "--metrics", ",".join(self.coreclr_args.metrics) ] + elif base_bytes is not None and diff_bytes is not None: + command += [ "--override-total-base-metric", str(base_bytes), "--override-total-diff-metric", str(diff_bytes) ] + run_and_log(command, logging.INFO) ran_jit_analyze = True @@ -2015,6 +2051,14 @@ async def create_one_artifact(jit_path: str, location: str, flags) -> str: os.remove(fail_mcl_file) fail_mcl_file = None + if os.path.isfile(base_metrics_summary_file): + os.remove(base_metrics_summary_file) + base_metrics_summary_file = None + + if os.path.isfile(diff_metrics_summary_file): + os.remove(diff_metrics_summary_file) + diff_metrics_summary_file = None + ################################################################################################ end of for mch_file in self.mch_files # Report the overall results summary of the asmdiffs run From 8962551d079456c47ad10d428150674884e3d1cb Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Fri, 8 Oct 2021 08:07:38 +0200 Subject: [PATCH 002/106] [wasm] Do not strip dotnet.wasm in Debug build (#60134) * [wasm] Do not strip dotnet.wasm in Debug build In https://github.com/dotnet/runtime/commit/909880b5a7a737f86d388f263e0bfa8c2ddb186c we started stripping `dotnet.wasm` unconditionally. Make it conditional again. wa-info output shows the name and debug sections are now present in Debug builds: Module: path: .\artifacts\bin\native\net7.0-Browser-Debug-wasm\dotnet.wasm size: 11,490,538 binary format version: 1 sections: 17 ... id: Custom name: name size: 819,487 id: Custom name: .debug_info size: 3,029,986 id: Custom name: .debug_loc size: 148,406 id: Custom name: .debug_ranges size: 100,294 id: Custom name: .debug_abbrev size: 197,396 id: Custom name: .debug_line size: 2,733,460 id: Custom name: .debug_str size: 684,359 Module: path: .\artifacts\bin\native\net7.0-Browser-Release-wasm\dotnet.wasm size: 2,432,319 binary format version: 1 sections: 10 id: Type size: 2,140 id: Import size: 661 id: Function size: 8,868 id: Table size: 7 id: Memory size: 7 id: Global size: 19 id: Export size: 383 id: Element size: 8,296 id: Code size: 1,881,152 id: Data size: 530,749 * Do not set EMSDK_PATH in Release build --- src/mono/wasm/runtime/CMakeLists.txt | 4 +++- src/mono/wasm/wasm.proj | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/mono/wasm/runtime/CMakeLists.txt b/src/mono/wasm/runtime/CMakeLists.txt index 75660764b7f85..af8699ecca199 100644 --- a/src/mono/wasm/runtime/CMakeLists.txt +++ b/src/mono/wasm/runtime/CMakeLists.txt @@ -28,4 +28,6 @@ set_target_properties(dotnet PROPERTIES LINK_FLAGS "@${NATIVE_BIN_DIR}/src/emcc-default.rsp ${CONFIGURATION_EMCC_FLAGS} -DENABLE_NETCORE=1 --pre-js ${NATIVE_BIN_DIR}/src/runtime.iffe.js --js-library ${SOURCE_DIR}/library-dotnet.js --js-library ${SYSTEM_NATIVE_DIR}/pal_random.js" RUNTIME_OUTPUT_DIRECTORY "${NATIVE_BIN_DIR}") -add_custom_command(TARGET dotnet POST_BUILD COMMAND ${EMSDK_PATH}/upstream/bin/wasm-opt --strip-dwarf ${NATIVE_BIN_DIR}/dotnet.wasm -o ${NATIVE_BIN_DIR}/dotnet.wasm) +if(CMAKE_BUILD_TYPE STREQUAL "Release") + add_custom_command(TARGET dotnet POST_BUILD COMMAND ${EMSDK_PATH}/upstream/bin/wasm-opt --strip-dwarf ${NATIVE_BIN_DIR}/dotnet.wasm -o ${NATIVE_BIN_DIR}/dotnet.wasm) +endif() diff --git a/src/mono/wasm/wasm.proj b/src/mono/wasm/wasm.proj index aca83e368927c..1dcbf4bf9f206 100644 --- a/src/mono/wasm/wasm.proj +++ b/src/mono/wasm/wasm.proj @@ -150,7 +150,9 @@ $(ArtifactsObjDir)wasm/pinvoke-table.h -g -Os -s -DDEBUG=1 -Oz - emcmake cmake $(MSBuildThisFileDirectory)runtime -DCONFIGURATION_EMCC_FLAGS="$(CMakeConfigurationEmccFlags)" -DMONO_INCLUDES="$(MonoArtifactsPath)include/mono-2.0" -DMONO_OBJ_INCLUDES="$(MonoObjDir.TrimEnd('\/'))" -DICU_LIB_DIR="$(ICULibDir.TrimEnd('\/'))" -DMONO_ARTIFACTS_DIR="$(MonoArtifactsPath.TrimEnd('\/'))" -DNATIVE_BIN_DIR="$(NativeBinDir.TrimEnd('\/'))" -DSYSTEM_NATIVE_DIR="$(RepoRoot)src/libraries/Native/Unix/System.Native" -DEMSDK_PATH="$(EMSDK_PATH.TrimEnd('\/'))" -DSOURCE_DIR="$(MSBuildThisFileDirectory.TrimEnd('\/'))/runtime" + -DEMSDK_PATH="$(EMSDK_PATH.TrimEnd('\/'))" + emcmake cmake $(MSBuildThisFileDirectory)runtime -DCONFIGURATION_EMCC_FLAGS="$(CMakeConfigurationEmccFlags)" -DMONO_INCLUDES="$(MonoArtifactsPath)include/mono-2.0" -DMONO_OBJ_INCLUDES="$(MonoObjDir.TrimEnd('\/'))" -DICU_LIB_DIR="$(ICULibDir.TrimEnd('\/'))" -DMONO_ARTIFACTS_DIR="$(MonoArtifactsPath.TrimEnd('\/'))" -DNATIVE_BIN_DIR="$(NativeBinDir.TrimEnd('\/'))" -DSYSTEM_NATIVE_DIR="$(RepoRoot)src/libraries/Native/Unix/System.Native" -DSOURCE_DIR="$(MSBuildThisFileDirectory.TrimEnd('\/'))/runtime"$(CMakeConfigurationEmsdkPath) + emcmake cmake $(MSBuildThisFileDirectory)runtime -DCONFIGURATION_EMCC_FLAGS="$(CMakeConfigurationEmccFlags)" -DMONO_INCLUDES="$(MonoArtifactsPath)include/mono-2.0" -DMONO_OBJ_INCLUDES="$(MonoObjDir.TrimEnd('\/'))" -DICU_LIB_DIR="$(ICULibDir.TrimEnd('\/'))" -DMONO_ARTIFACTS_DIR="$(MonoArtifactsPath.TrimEnd('\/'))" -DNATIVE_BIN_DIR="$(NativeBinDir.TrimEnd('\/'))" -DSYSTEM_NATIVE_DIR="$(RepoRoot)src/libraries/Native/Unix/System.Native" -DEMSDK_PATH="$(EMSDK_PATH.TrimEnd('\/'))" -DSOURCE_DIR="$(MSBuildThisFileDirectory.TrimEnd('\/'))/runtime" call "$(RepositoryEngineeringDir)native\init-vs-env.cmd" && call "$([MSBuild]::NormalizePath('$(EMSDK_PATH)', 'emsdk_env.bat'))" && $(CMakeBuildRuntimeConfigureCmd) bash -c 'source $(EMSDK_PATH)/emsdk_env.sh 2>&1 && $(CMakeBuildRuntimeConfigureCmd)' From 40ea97591e42951e2da96016cb0ee14664515123 Mon Sep 17 00:00:00 2001 From: Johan Lorensson Date: Fri, 8 Oct 2021 09:03:19 +0200 Subject: [PATCH 003/106] Disable rundownvalidation runtime test on MonoVM (#60117) Disable rundownvalidation runtime test on MonoVM while working on local repro. --- src/tests/issues.targets | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tests/issues.targets b/src/tests/issues.targets index d1e7163478ab3..7b090ae247847 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -1492,6 +1492,9 @@ https://github.com/dotnet/runtime/issues/59296 + + https://github.com/dotnet/runtime/issues/54801 + needs triage From 2880cc1d70b19933cd8cedd7d077fb3e42a95304 Mon Sep 17 00:00:00 2001 From: Fan Yang <52458914+fanyang-mono@users.noreply.github.com> Date: Fri, 8 Oct 2021 06:34:59 -0400 Subject: [PATCH 004/106] Stop running runtime tests on Android arm64 in main build (#59772) --- eng/pipelines/runtime-staging.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/eng/pipelines/runtime-staging.yml b/eng/pipelines/runtime-staging.yml index 580c695a01319..92cca13420841 100644 --- a/eng/pipelines/runtime-staging.yml +++ b/eng/pipelines/runtime-staging.yml @@ -401,12 +401,13 @@ jobs: eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true), eq(variables['isFullMatrix'], true)) # 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) + # Turn off the testing for now, until https://github.com/dotnet/xharness/issues/663 gets resolved + # ${{ 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 From 971479e2a4d323ecc951d7135b9437582160fdf8 Mon Sep 17 00:00:00 2001 From: Peter Sollich Date: Fri, 8 Oct 2021 14:10:38 +0200 Subject: [PATCH 005/106] Fix issue in decommit_ephemeral_segment_pages (segment case). (#59989) We assume that we can use half the free list space in gen 0 for new allocation. If that is too optimistic, we may allocate into decommitted memory and crash in the allocator. That is because there is a race condition between the allocating thread and the decommitting thread - we decided to avoid that by making sure we would never decommit memory that we may allocate in gen 0. There are two reasons why assuming we can use half the free list space for new allocations may be too optimistic: - if we allocate large objects in gen 0, we may not have free spaces of the necessary size available. - when background GC goes into background_ephemeral_sweep, it deletes and rebuilds the free list for gen 0. A thread trying to allocate during that time may see a completely empty free list. --- 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 d041ea52d64d1..c73321be10a4f 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -38748,7 +38748,7 @@ void gc_heap::decommit_ephemeral_segment_pages() dynamic_data* dd0 = dynamic_data_of (0); - ptrdiff_t desired_allocation = estimate_gen_growth (soh_gen0) + + ptrdiff_t desired_allocation = dd_new_allocation (dd0) + estimate_gen_growth (soh_gen1) + loh_size_threshold; From edc5a414f0c7e9ee59f87e39c5258f35c23747d2 Mon Sep 17 00:00:00 2001 From: Tom Deseyn Date: Fri, 8 Oct 2021 15:07:19 +0200 Subject: [PATCH 006/106] Socket: don't disconnect Socket for unknown/unsupported socket options. (#59925) Fixes #59055. --- .../src/System/Net/Sockets/Socket.cs | 46 ++++--- .../FunctionalTests/SocketOptionNameTest.cs | 126 ++++++++++++++++++ 2 files changed, 153 insertions(+), 19 deletions(-) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs index 359078e59cd0b..c857ab33a1eee 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs @@ -1933,7 +1933,7 @@ public void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName opti // Throw an appropriate SocketException if the native call fails. if (errorCode != SocketError.Success) { - UpdateStatusAfterSocketErrorAndThrowException(errorCode); + UpdateStatusAfterSocketOptionErrorAndThrowException(errorCode); } } @@ -2015,7 +2015,7 @@ public void SetRawSocketOption(int optionLevel, int optionName, ReadOnlySpan option if (errorCode != SocketError.Success) { - UpdateStatusAfterSocketErrorAndThrowException(errorCode); + UpdateStatusAfterSocketOptionErrorAndThrowException(errorCode); } return realOptionLength; @@ -3432,7 +3432,7 @@ internal unsafe void SetSocketOption(SocketOptionLevel optionLevel, SocketOption // Throw an appropriate SocketException if the native call fails. if (errorCode != SocketError.Success) { - UpdateStatusAfterSocketErrorAndThrowException(errorCode); + UpdateStatusAfterSocketOptionErrorAndThrowException(errorCode); } } @@ -3445,7 +3445,7 @@ private void SetMulticastOption(SocketOptionName optionName, MulticastOption MR) // Throw an appropriate SocketException if the native call fails. if (errorCode != SocketError.Success) { - UpdateStatusAfterSocketErrorAndThrowException(errorCode); + UpdateStatusAfterSocketOptionErrorAndThrowException(errorCode); } } @@ -3472,7 +3472,7 @@ private void SetLingerOption(LingerOption lref) // Throw an appropriate SocketException if the native call fails. if (errorCode != SocketError.Success) { - UpdateStatusAfterSocketErrorAndThrowException(errorCode); + UpdateStatusAfterSocketOptionErrorAndThrowException(errorCode); } } @@ -3486,7 +3486,7 @@ private void SetLingerOption(LingerOption lref) // Throw an appropriate SocketException if the native call fails. if (errorCode != SocketError.Success) { - UpdateStatusAfterSocketErrorAndThrowException(errorCode); + UpdateStatusAfterSocketOptionErrorAndThrowException(errorCode); } return lingerOption; @@ -3502,7 +3502,7 @@ private void SetLingerOption(LingerOption lref) // Throw an appropriate SocketException if the native call fails. if (errorCode != SocketError.Success) { - UpdateStatusAfterSocketErrorAndThrowException(errorCode); + UpdateStatusAfterSocketOptionErrorAndThrowException(errorCode); } return multicastOption; @@ -3519,7 +3519,7 @@ private void SetLingerOption(LingerOption lref) // Throw an appropriate SocketException if the native call fails. if (errorCode != SocketError.Success) { - UpdateStatusAfterSocketErrorAndThrowException(errorCode); + UpdateStatusAfterSocketOptionErrorAndThrowException(errorCode); } return multicastOption; @@ -3690,11 +3690,19 @@ internal void SetToDisconnected() } } - private void UpdateStatusAfterSocketErrorAndThrowException(SocketError error, [CallerMemberName] string? callerName = null) + private void UpdateStatusAfterSocketOptionErrorAndThrowException(SocketError error, [CallerMemberName] string? callerName = null) + { + // Don't disconnect socket for unknown options. + bool disconnectOnFailure = error != SocketError.ProtocolOption && + error != SocketError.OperationNotSupported; + UpdateStatusAfterSocketErrorAndThrowException(error, disconnectOnFailure, callerName); + } + + private void UpdateStatusAfterSocketErrorAndThrowException(SocketError error, bool disconnectOnFailure = true, [CallerMemberName] string? callerName = null) { // Update the internal state of this socket according to the error before throwing. var socketException = new SocketException((int)error); - UpdateStatusAfterSocketError(socketException); + UpdateStatusAfterSocketError(socketException, disconnectOnFailure); if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, socketException, memberName: callerName); throw socketException; } @@ -3702,18 +3710,18 @@ private void UpdateStatusAfterSocketErrorAndThrowException(SocketError error, [C // UpdateStatusAfterSocketError(socketException) - updates the status of a connected socket // on which a failure occurred. it'll go to winsock and check if the connection // is still open and if it needs to update our internal state. - internal void UpdateStatusAfterSocketError(SocketException socketException) + internal void UpdateStatusAfterSocketError(SocketException socketException, bool disconnectOnFailure = true) { - UpdateStatusAfterSocketError(socketException.SocketErrorCode); + UpdateStatusAfterSocketError(socketException.SocketErrorCode, disconnectOnFailure); } - internal void UpdateStatusAfterSocketError(SocketError errorCode) + internal void UpdateStatusAfterSocketError(SocketError errorCode, bool disconnectOnFailure = true) { // If we already know the socket is disconnected // we don't need to do anything else. - if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, $"errorCode:{errorCode}"); + if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, $"errorCode:{errorCode}, disconnectOnFailure:{disconnectOnFailure}"); - if (_isConnected && (_handle.IsInvalid || (errorCode != SocketError.WouldBlock && + if (disconnectOnFailure && _isConnected && (_handle.IsInvalid || (errorCode != SocketError.WouldBlock && errorCode != SocketError.IOPending && errorCode != SocketError.NoBufferSpaceAvailable && errorCode != SocketError.TimedOut))) { diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketOptionNameTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketOptionNameTest.cs index e6868e4a6fbeb..5073f60c3e6a1 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketOptionNameTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketOptionNameTest.cs @@ -562,6 +562,132 @@ public void Get_AcceptConnection_Succeeds() } } + [Fact] + public void GetUnsupportedSocketOption_DoesNotDisconnectSocket() + { + (Socket socket1, Socket socket2) = SocketTestExtensions.CreateConnectedSocketPair(); + using (socket1) + using (socket2) + { + SocketException se = Assert.Throws(() => socket1.GetSocketOption(SocketOptionLevel.Socket, (SocketOptionName)(-1))); + Assert.True(se.SocketErrorCode == SocketError.ProtocolOption || + se.SocketErrorCode == SocketError.OperationNotSupported, $"SocketError: {se.SocketErrorCode}"); + + Assert.True(socket1.Connected, "Connected"); + } + } + + [Fact] + public void GetUnsupportedSocketOptionBytesArg_DoesNotDisconnectSocket() + { + (Socket socket1, Socket socket2) = SocketTestExtensions.CreateConnectedSocketPair(); + using (socket1) + using (socket2) + { + var optionValue = new byte[4]; + SocketException se = Assert.Throws(() => socket1.GetSocketOption(SocketOptionLevel.Socket, (SocketOptionName)(-1), optionValue)); + Assert.True(se.SocketErrorCode == SocketError.ProtocolOption || + se.SocketErrorCode == SocketError.OperationNotSupported, $"SocketError: {se.SocketErrorCode}"); + + Assert.True(socket1.Connected, "Connected"); + } + } + + [Fact] + public void GetUnsupportedSocketOptionLengthArg_DoesNotDisconnectSocket() + { + (Socket socket1, Socket socket2) = SocketTestExtensions.CreateConnectedSocketPair(); + using (socket1) + using (socket2) + { + SocketException se = Assert.Throws(() => socket1.GetSocketOption(SocketOptionLevel.Socket, (SocketOptionName)(-1), optionLength: 4)); + Assert.True(se.SocketErrorCode == SocketError.ProtocolOption || + se.SocketErrorCode == SocketError.OperationNotSupported, $"SocketError: {se.SocketErrorCode}"); + + Assert.True(socket1.Connected, "Connected"); + } + } + + [Fact] + public void SetUnsupportedSocketOptionIntArg_DoesNotDisconnectSocket() + { + (Socket socket1, Socket socket2) = SocketTestExtensions.CreateConnectedSocketPair(); + using (socket1) + using (socket2) + { + SocketException se = Assert.Throws(() => socket1.SetSocketOption(SocketOptionLevel.Socket, (SocketOptionName)(-1), optionValue: 1)); + Assert.True(se.SocketErrorCode == SocketError.ProtocolOption || + se.SocketErrorCode == SocketError.OperationNotSupported, $"SocketError: {se.SocketErrorCode}"); + + Assert.True(socket1.Connected, "Connected"); + } + } + + [Fact] + public void SetUnsupportedSocketOptionBytesArg_DoesNotDisconnectSocket() + { + (Socket socket1, Socket socket2) = SocketTestExtensions.CreateConnectedSocketPair(); + using (socket1) + using (socket2) + { + var optionValue = new byte[4]; + SocketException se = Assert.Throws(() => socket1.SetSocketOption(SocketOptionLevel.Socket, (SocketOptionName)(-1), optionValue)); + Assert.True(se.SocketErrorCode == SocketError.ProtocolOption || + se.SocketErrorCode == SocketError.OperationNotSupported, $"SocketError: {se.SocketErrorCode}"); + + Assert.True(socket1.Connected, "Connected"); + } + } + + [Fact] + public void SetUnsupportedSocketOptionBoolArg_DoesNotDisconnectSocket() + { + (Socket socket1, Socket socket2) = SocketTestExtensions.CreateConnectedSocketPair(); + using (socket1) + using (socket2) + { + bool optionValue = true; + SocketException se = Assert.Throws(() => socket1.SetSocketOption(SocketOptionLevel.Socket, (SocketOptionName)(-1), optionValue)); + Assert.True(se.SocketErrorCode == SocketError.ProtocolOption || + se.SocketErrorCode == SocketError.OperationNotSupported, $"SocketError: {se.SocketErrorCode}"); + + Assert.True(socket1.Connected, "Connected"); + } + } + + [Fact] + public void GetUnsupportedRawSocketOption_DoesNotDisconnectSocket() + { + (Socket socket1, Socket socket2) = SocketTestExtensions.CreateConnectedSocketPair(); + using (socket1) + using (socket2) + { + var optionValue = new byte[4]; + SocketException se = Assert.Throws(() => socket1.GetRawSocketOption(SOL_SOCKET, -1, optionValue)); + Assert.True(se.SocketErrorCode == SocketError.ProtocolOption || + se.SocketErrorCode == SocketError.OperationNotSupported, $"SocketError: {se.SocketErrorCode}"); + + Assert.True(socket1.Connected, "Connected"); + } + } + + [Fact] + public void SetUnsupportedRawSocketOption_DoesNotDisconnectSocket() + { + (Socket socket1, Socket socket2) = SocketTestExtensions.CreateConnectedSocketPair(); + using (socket1) + using (socket2) + { + var optionValue = new byte[4]; + SocketException se = Assert.Throws(() => socket1.SetRawSocketOption(SOL_SOCKET, -1, optionValue)); + Assert.True(se.SocketErrorCode == SocketError.ProtocolOption || + se.SocketErrorCode == SocketError.OperationNotSupported, $"SocketError: {se.SocketErrorCode}"); + + Assert.True(socket1.Connected, "Connected"); + } + } + + private static int SOL_SOCKET = OperatingSystem.IsLinux() ? 1 : (int)SocketOptionLevel.Socket; } [Collection("NoParallelTests")] From de00deb924ebfc4418837002b60a1d31734b7e8d Mon Sep 17 00:00:00 2001 From: SingleAccretion <62474226+SingleAccretion@users.noreply.github.com> Date: Fri, 8 Oct 2021 16:21:18 +0300 Subject: [PATCH 007/106] Use `IntegralRange` in `fgOptimizeCast` (#59897) * Set call return type in fgMorphIntoHelperCall It was just forgotten. * Introduce IntegralRange::ForNode * Use Range::ForNode in assertion propagation Couple good diffs from the handling of GT_CALL. * Fix IntegralRange::ForCastInput The comments are right, the code is wrong... Fortunately, the bug was a pessimizing one, not a correctness issue. * Use IntegralRange in fgOptimizeCast And reap the benefits. --- src/coreclr/jit/assertionprop.cpp | 138 ++++++++++--------- src/coreclr/jit/compiler.h | 8 +- src/coreclr/jit/gentree.h | 5 + src/coreclr/jit/morph.cpp | 213 ++++++++---------------------- 4 files changed, 136 insertions(+), 228 deletions(-) diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index d0395283eb384..d9a450bcbf8c1 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -118,6 +118,59 @@ bool IntegralRange::Contains(int64_t value) const } } +//------------------------------------------------------------------------ +// ForNode: Compute the integral range for a node. +// +// Arguments: +// node - the node, of an integral type, in question +// compiler - the Compiler, used to retrieve additional info +// +// Return Value: +// The integral range this node produces. +// +/* static */ IntegralRange IntegralRange::ForNode(GenTree* node, Compiler* compiler) +{ + assert(varTypeIsIntegral(node)); + + var_types rangeType = node->TypeGet(); + + switch (node->OperGet()) + { + case GT_EQ: + case GT_NE: + case GT_LT: + case GT_LE: + case GT_GE: + case GT_GT: + return {SymbolicIntegerValue::Zero, SymbolicIntegerValue::One}; + + case GT_ARR_LENGTH: + return {SymbolicIntegerValue::Zero, SymbolicIntegerValue::IntMax}; + + case GT_CALL: + if (node->AsCall()->NormalizesSmallTypesOnReturn()) + { + rangeType = static_cast(node->AsCall()->gtReturnType); + } + break; + + case GT_LCL_VAR: + if (compiler->lvaGetDesc(node->AsLclVar())->lvNormalizeOnStore()) + { + rangeType = compiler->lvaGetDesc(node->AsLclVar())->TypeGet(); + } + break; + + case GT_CAST: + return ForCastOutput(node->AsCast()); + + default: + break; + } + + return ForType(rangeType); +} + //------------------------------------------------------------------------ // ForCastInput: Get the non-overflowing input range for a cast. // @@ -214,7 +267,14 @@ bool IntegralRange::Contains(int64_t value) const // CAST_OVF(long <- ulong) - [0..LONG_MAX] // CAST_OVF(long <- long) - [LONG_MIN..LONG_MAX] case TYP_LONG: - lowerBound = fromUnsigned ? SymbolicIntegerValue::Zero : LowerBoundForType(fromType); + if (fromUnsigned && (fromType == TYP_LONG)) + { + lowerBound = SymbolicIntegerValue::Zero; + } + else + { + lowerBound = LowerBoundForType(fromType); + } upperBound = UpperBoundForType(fromType); break; @@ -1545,12 +1605,17 @@ AssertionIndex Compiler::optCreateAssertion(GenTree* op1, } // Try and see if we can make a subrange assertion. - if (((assertionKind == OAK_SUBRANGE) || (assertionKind == OAK_EQUAL))) + if (((assertionKind == OAK_SUBRANGE) || (assertionKind == OAK_EQUAL)) && varTypeIsIntegral(op2)) { - if (optTryExtractSubrangeAssertion(op2, &assertion.op2.u2)) + IntegralRange nodeRange = IntegralRange::ForNode(op2, this); + IntegralRange typeRange = IntegralRange::ForType(genActualType(op2)); + assert(typeRange.Contains(nodeRange)); + + if (!typeRange.Equals(nodeRange)) { assertion.op2.kind = O2K_SUBRANGE; assertion.assertionKind = OAK_SUBRANGE; + assertion.op2.u2 = nodeRange; } } } @@ -1689,73 +1754,6 @@ AssertionIndex Compiler::optFinalizeCreatingAssertion(AssertionDsc* assertion) return optAddAssertion(assertion); } -//------------------------------------------------------------------------ -// optTryExtractSubrangeAssertion: Extract the bounds of the value a tree produces. -// -// Generates [0..1] ranges for relops, [T_MIN..T_MAX] for small-typed indirections -// and casts to small types. Generates various ranges for casts to and from "large" -// types - see "IntegralRange::ForCastOutput". -// -// Arguments: -// source - tree producing the value -// pRange - [out] parameter for the range -// -// Return Value: -// "true" if the "source" computes a value that could be used for a subrange -// assertion, i. e. narrower than the range of the node's type. -// -// Notes: -// The "pRange" parameter is only written to if the function returns "true". -// -bool Compiler::optTryExtractSubrangeAssertion(GenTree* source, IntegralRange* pRange) -{ - var_types sourceType = TYP_UNDEF; - - switch (source->OperGet()) - { - case GT_EQ: - case GT_NE: - case GT_LT: - case GT_LE: - case GT_GT: - case GT_GE: - *pRange = {SymbolicIntegerValue::Zero, SymbolicIntegerValue::One}; - return true; - - case GT_CLS_VAR: - case GT_LCL_FLD: - case GT_IND: - sourceType = source->TypeGet(); - break; - - case GT_CAST: - if (varTypeIsIntegral(source)) - { - IntegralRange castRange = IntegralRange::ForCastOutput(source->AsCast()); - IntegralRange nodeRange = IntegralRange::ForType(source->TypeGet()); - assert(nodeRange.Contains(castRange)); - - if (!castRange.Equals(nodeRange)) - { - *pRange = castRange; - return true; - } - } - return false; - - default: - return false; - } - - if (varTypeIsSmall(sourceType)) - { - *pRange = IntegralRange::ForType(sourceType); - return true; - } - - return false; -} - /***************************************************************************** * * If tree is a constant node holding an integral value, retrieve the value in diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 7019714bed642..8ea6a7357f5ac 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -1216,6 +1216,11 @@ class IntegralRange return (m_lowerBound <= other.m_lowerBound) && (other.m_upperBound <= m_upperBound); } + bool IsPositive() + { + return m_lowerBound >= SymbolicIntegerValue::Zero; + } + bool Equals(IntegralRange other) const { return (m_lowerBound == other.m_lowerBound) && (m_upperBound == other.m_upperBound); @@ -1230,6 +1235,7 @@ class IntegralRange return {LowerBoundForType(type), UpperBoundForType(type)}; } + static IntegralRange ForNode(GenTree* node, Compiler* compiler); static IntegralRange ForCastInput(GenTreeCast* cast); static IntegralRange ForCastOutput(GenTreeCast* cast); @@ -6363,7 +6369,7 @@ class Compiler GenTree* fgMorphCopyBlock(GenTree* tree); GenTree* fgMorphForRegisterFP(GenTree* tree); GenTree* fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac = nullptr); - GenTree* fgOptimizeCast(GenTree* tree); + GenTree* fgOptimizeCast(GenTreeCast* cast); GenTree* fgOptimizeEqualityComparisonWithConst(GenTreeOp* cmp); GenTree* fgOptimizeRelationalComparisonWithConst(GenTreeOp* cmp); GenTree* fgPropagateCommaThrow(GenTree* parent, GenTreeOp* commaThrow, GenTreeFlags precedingSideEffects); diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index db7c56072130e..52844499e134b 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -4636,6 +4636,11 @@ struct GenTreeCall final : public GenTree } #endif // !FEATURE_TAILCALL_OPT + bool NormalizesSmallTypesOnReturn() + { + return GetUnmanagedCallConv() == CorInfoCallConvExtension::Managed; + } + bool IsSameThis() const { return (gtCallMoreFlags & GTF_CALL_M_NONVIRT_SAME_THIS) != 0; diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index a9a78198cb288..64b98336125ec 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -62,9 +62,9 @@ GenTree* Compiler::fgMorphIntoHelperCall(GenTree* tree, int helper, GenTreeCall: // The helper call ought to be semantically equivalent to the original node, so preserve its VN. tree->ChangeOper(GT_CALL, GenTree::PRESERVE_VN); - GenTreeCall* call = tree->AsCall(); - + GenTreeCall* call = tree->AsCall(); call->gtCallType = CT_HELPER; + call->gtReturnType = static_cast(tree->TypeGet()); call->gtCallMethHnd = eeFindHelper(helper); call->gtCallThisArg = nullptr; call->gtCallArgs = args; @@ -11998,7 +11998,7 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac) break; case GT_CAST: - tree = fgOptimizeCast(tree); + tree = fgOptimizeCast(tree->AsCast()); if (!tree->OperIsSimple()) { return tree; @@ -13251,195 +13251,94 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac) // Return Value: // The optimized tree (that can have any shape). // -GenTree* Compiler::fgOptimizeCast(GenTree* tree) +GenTree* Compiler::fgOptimizeCast(GenTreeCast* cast) { - GenTree* oper = tree->AsCast()->CastOp(); - var_types srcType = oper->TypeGet(); - var_types dstType = tree->CastToType(); - unsigned dstSize = genTypeSize(dstType); + GenTree* src = cast->CastOp(); - if (gtIsActiveCSE_Candidate(tree) || gtIsActiveCSE_Candidate(oper)) + if (gtIsActiveCSE_Candidate(cast) || gtIsActiveCSE_Candidate(src)) { - return tree; + return cast; } - /* See if we can discard the cast */ - if (varTypeIsIntegral(srcType) && varTypeIsIntegral(dstType)) + // See if we can discard the cast. + if (varTypeIsIntegral(cast) && varTypeIsIntegral(src)) { - if (tree->IsUnsigned() && !varTypeIsUnsigned(srcType)) + IntegralRange srcRange = IntegralRange::ForNode(src, this); + IntegralRange noOvfRange = IntegralRange::ForCastInput(cast); + + if (noOvfRange.Contains(srcRange)) { - if (varTypeIsSmall(srcType)) + // Casting between same-sized types is a no-op, + // given we have proven this cast cannot overflow. + if (genActualType(cast) == genActualType(src)) { - // Small signed values are automatically sign extended to TYP_INT. If the cast is interpreting the - // resulting TYP_INT value as unsigned then the "sign" bits end up being "value" bits and srcType - // must be TYP_UINT, not the original small signed type. Otherwise "conv.ovf.i2.un(i1(-1))" is - // wrongly treated as a widening conversion from i1 to i2 when in fact it is a narrowing conversion - // from u4 to i2. - srcType = genActualType(srcType); + return src; } - srcType = varTypeToUnsigned(srcType); - } + cast->ClearOverflow(); + cast->SetAllEffectsFlags(src); - if (srcType == dstType) - { // Certainly if they are identical it is pointless - goto REMOVE_CAST; + // Try and see if we can make this cast into a cheaper zero-extending version. + if (genActualTypeIsInt(src) && cast->TypeIs(TYP_LONG) && srcRange.IsPositive()) + { + cast->SetUnsigned(); + } } - if (oper->OperGet() == GT_LCL_VAR && varTypeIsSmall(dstType)) + // For checked casts, we're done. + if (cast->gtOverflow()) { - unsigned varNum = oper->AsLclVarCommon()->GetLclNum(); - LclVarDsc* varDsc = &lvaTable[varNum]; - if (varDsc->TypeGet() == dstType && varDsc->lvNormalizeOnStore()) - { - goto REMOVE_CAST; - } + return cast; } - bool unsignedSrc = varTypeIsUnsigned(srcType); - bool unsignedDst = varTypeIsUnsigned(dstType); - bool signsDiffer = (unsignedSrc != unsignedDst); - unsigned srcSize = genTypeSize(srcType); + var_types castToType = cast->CastToType(); - // For same sized casts with - // the same signs or non-overflow cast we discard them as well - if (srcSize == dstSize) + // For indir-like nodes, we may be able to change their type to satisfy (and discard) the cast. + if (varTypeIsSmall(castToType) && (genTypeSize(castToType) == genTypeSize(src)) && + src->OperIs(GT_IND, GT_CLS_VAR, GT_LCL_FLD)) { - // This should have been handled by "fgMorphExpandCast". - noway_assert(varTypeIsGC(srcType) == varTypeIsGC(dstType)); + // We're changing the type here so we need to update the VN; + // in other cases we discard the cast without modifying src + // so the VN doesn't change. - if (!signsDiffer) - { - goto REMOVE_CAST; - } + src->ChangeType(castToType); + src->SetVNsFromNode(cast); - if (!tree->gtOverflow()) - { - /* For small type casts, when necessary we force - the src operand to the dstType and allow the - implied load from memory to perform the casting */ - if (varTypeIsSmall(srcType)) - { - switch (oper->gtOper) - { - case GT_IND: - case GT_CLS_VAR: - case GT_LCL_FLD: - oper->gtType = dstType; - // We're changing the type here so we need to update the VN; - // in other cases we discard the cast without modifying oper - // so the VN doesn't change. - oper->SetVNsFromNode(tree); - goto REMOVE_CAST; - default: - break; - } - } - else - { - goto REMOVE_CAST; - } - } + return src; } - else if (srcSize < dstSize) // widening cast - { - // Keep any long casts - if (dstSize == sizeof(int)) - { - // Only keep signed to unsigned widening cast with overflow check - if (!tree->gtOverflow() || !unsignedDst || unsignedSrc) - { - goto REMOVE_CAST; - } - } - // Widening casts from unsigned or to signed can never overflow + // Try to narrow the operand of the cast and discard the cast. + if (opts.OptEnabled(CLFLG_TREETRANS) && (genTypeSize(src) > genTypeSize(castToType)) && + optNarrowTree(src, src->TypeGet(), castToType, cast->gtVNPair, false)) + { + optNarrowTree(src, src->TypeGet(), castToType, cast->gtVNPair, true); - if (unsignedSrc || !unsignedDst) + // "optNarrowTree" may leave a dead cast behind. + if (src->OperIs(GT_CAST) && (src->AsCast()->CastToType() == genActualType(src->AsCast()->CastOp()))) { - tree->ClearOverflow(); - if ((oper->gtFlags & GTF_EXCEPT) == GTF_EMPTY) - { - tree->gtFlags &= ~GTF_EXCEPT; - } + src = src->AsCast()->CastOp(); } - } - else // if (srcSize > dstSize) - { - // Try to narrow the operand of the cast and discard the cast - // Note: Do not narrow a cast that is marked as a CSE - // And do not narrow if the oper is marked as a CSE either - // - if (!tree->gtOverflow() && opts.OptEnabled(CLFLG_TREETRANS) && - optNarrowTree(oper, srcType, dstType, tree->gtVNPair, false)) - { - optNarrowTree(oper, srcType, dstType, tree->gtVNPair, true); - /* If oper is changed into a cast to TYP_INT, or to a GT_NOP, we may need to discard it */ - if (oper->gtOper == GT_CAST && oper->CastToType() == genActualType(oper->CastFromType())) - { - oper = oper->AsCast()->CastOp(); - } - goto REMOVE_CAST; - } + return src; } - } - // Check for two consecutive casts into the same dstType. - // Also check for consecutive casts to small types. - if (oper->OperIs(GT_CAST) && !tree->gtOverflow()) - { - var_types dstCastToType = dstType; - var_types srcCastToType = oper->CastToType(); - if (dstCastToType == srcCastToType) + // Check for two consecutive casts, we may be able to discard the intermediate one. + if (opts.OptimizationEnabled() && src->OperIs(GT_CAST) && !src->gtOverflow()) { - goto REMOVE_CAST; - } - // We can take advantage of the implicit zero/sign-extension for - // small integer types and eliminate some casts. - if (opts.OptimizationEnabled() && !oper->gtOverflow() && varTypeIsSmall(dstCastToType) && - varTypeIsSmall(srcCastToType)) - { - // Gather some facts about our casts. - bool srcZeroExtends = varTypeIsUnsigned(srcCastToType); - bool dstZeroExtends = varTypeIsUnsigned(dstCastToType); - unsigned srcCastToSize = genTypeSize(srcCastToType); - unsigned dstCastToSize = genTypeSize(dstCastToType); + var_types dstCastToType = castToType; + var_types srcCastToType = src->AsCast()->CastToType(); - // If the previous cast to a smaller type was zero-extending, - // this cast will also always be zero-extending. Example: - // CAST(ubyte): 000X => CAST(short): Sign-extend(0X) => 000X. - if (srcZeroExtends && (dstCastToSize > srcCastToSize)) + // CAST(ubyte <- CAST(short <- X)): CAST(ubyte <- X). + // CAST(ushort <- CAST(short <- X)): CAST(ushort <- X). + if (varTypeIsSmall(srcCastToType) && (genTypeSize(dstCastToType) <= genTypeSize(srcCastToType))) { - dstZeroExtends = true; - } - - // Case #1: cast to a smaller or equal in size type. - // We can discard the intermediate cast. Examples: - // CAST(short): --XX => CAST(ubyte): 000X. - // CAST(ushort): 00XX => CAST(short): --XX. - if (dstCastToSize <= srcCastToSize) - { - tree->AsCast()->CastOp() = oper->AsCast()->CastOp(); - DEBUG_DESTROY_NODE(oper); - } - // Case #2: cast to a larger type with the same effect. - // Here we can eliminate the outer cast. Example: - // CAST(byte): ---X => CAST(short): Sign-extend(-X) => ---X. - // Example of a sequence where this does not hold: - // CAST(byte): ---X => CAST(ushort): Zero-extend(-X) => 00-X. - else if (srcZeroExtends == dstZeroExtends) - { - goto REMOVE_CAST; + cast->CastOp() = src->AsCast()->CastOp(); + DEBUG_DESTROY_NODE(src); } } } - return tree; - -REMOVE_CAST: - DEBUG_DESTROY_NODE(tree); - return oper; + return cast; } //------------------------------------------------------------------------ From cc7f2f9c1a80c262055757331acca25d2748d381 Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Fri, 8 Oct 2021 09:12:11 -0500 Subject: [PATCH 008/106] Check BackgroundService.ExecuteTask for null (#60139) * Check BackgroundService.ExecuteTask for null In some scenarios, derived classes might not have called base.StartAsync, and ExecuteTask will still be null. Ensure we don't fail in those cases. Fix #60131 --- .../src/Internal/Host.cs | 11 +++++- .../tests/UnitTests/Internal/HostTests.cs | 39 +++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/Internal/Host.cs b/src/libraries/Microsoft.Extensions.Hosting/src/Internal/Host.cs index 4041133a0a9f2..2f73146e695ef 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/Internal/Host.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/src/Internal/Host.cs @@ -79,15 +79,22 @@ public async Task StartAsync(CancellationToken cancellationToken = default) private async Task TryExecuteBackgroundServiceAsync(BackgroundService backgroundService) { + // backgroundService.ExecuteTask may not be set (e.g. if the derived class doesn't call base.StartAsync) + Task backgroundTask = backgroundService.ExecuteTask; + if (backgroundTask == null) + { + return; + } + try { - await backgroundService.ExecuteTask.ConfigureAwait(false); + await backgroundTask.ConfigureAwait(false); } catch (Exception ex) { // When the host is being stopped, it cancels the background services. // This isn't an error condition, so don't log it as an error. - if (_stopCalled && backgroundService.ExecuteTask.IsCanceled && ex is OperationCanceledException) + if (_stopCalled && backgroundTask.IsCanceled && ex is OperationCanceledException) { return; } diff --git a/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/Internal/HostTests.cs b/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/Internal/HostTests.cs index d07c2bc57eaa7..b3133dba3be88 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/Internal/HostTests.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/Internal/HostTests.cs @@ -1394,6 +1394,36 @@ public async Task HostNoErrorWhenServiceIsCanceledAsPartOfStop() } } + /// + /// Tests that when a BackgroundService does not call base, the Host still starts and stops successfully. + /// + [Fact] + public async Task StartOnBackgroundServiceThatDoesNotCallBase() + { + TestLoggerProvider logger = new TestLoggerProvider(); + + using IHost host = CreateBuilder() + .ConfigureLogging(logging => + { + logging.AddProvider(logger); + }) + .ConfigureServices(services => + { + services.AddHostedService(); + }) + .Build(); + + host.Start(); + await host.StopAsync(); + + foreach (LogEvent logEvent in logger.GetEvents()) + { + Assert.True(logEvent.LogLevel <= LogLevel.Information, "All logged events should be less than or equal to Information. No Warnings or Errors."); + + Assert.NotEqual("BackgroundServiceFaulted", logEvent.EventId.Name); + } + } + private IHostBuilder CreateBuilder(IConfiguration config = null) { return new HostBuilder().ConfigureHostConfiguration(builder => builder.AddConfiguration(config ?? new ConfigurationBuilder().Build())); @@ -1562,5 +1592,14 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) } } } + + private class BackgroundServiceDoesNotCallBase : BackgroundService + { + public override Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask; + + protected override Task ExecuteAsync(CancellationToken stoppingToken) => Task.CompletedTask; + + public override Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; + } } } From 04f70d6fc56a5261b0dcad0e6217fcbf92570aaf Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Fri, 8 Oct 2021 16:38:09 +0200 Subject: [PATCH 009/106] [main] Update dependencies from 7 repositories (#59930) Co-authored-by: dotnet-maestro[bot] Co-authored-by: Marek Safar --- .config/dotnet-tools.json | 2 +- eng/Version.Details.xml | 184 +++++++++---------- eng/Versions.props | 82 ++++----- eng/common/native/init-compiler.sh | 4 +- eng/common/templates/steps/send-to-helix.yml | 3 - global.json | 10 +- 6 files changed, 141 insertions(+), 144 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 5e9013310e4e9..991875c53343e 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "microsoft.dotnet.xharness.cli": { - "version": "1.0.0-prerelease.21501.2", + "version": "1.0.0-prerelease.21508.1", "commands": [ "xharness" ] diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 44b371499f890..2fe8e174f10e8 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -18,121 +18,121 @@ - + https://github.com/dotnet/arcade - 3ea0d860c6973f2cbadc9e895c7ec2cbdaec4ad5 + 98bacf3d68679c51adbb7c596a3de16a7f9acacf - + https://github.com/dotnet/arcade - 3ea0d860c6973f2cbadc9e895c7ec2cbdaec4ad5 + 98bacf3d68679c51adbb7c596a3de16a7f9acacf - + https://github.com/dotnet/arcade - 3ea0d860c6973f2cbadc9e895c7ec2cbdaec4ad5 + 98bacf3d68679c51adbb7c596a3de16a7f9acacf - + https://github.com/dotnet/arcade - 3ea0d860c6973f2cbadc9e895c7ec2cbdaec4ad5 + 98bacf3d68679c51adbb7c596a3de16a7f9acacf - + https://github.com/dotnet/arcade - 3ea0d860c6973f2cbadc9e895c7ec2cbdaec4ad5 + 98bacf3d68679c51adbb7c596a3de16a7f9acacf - + https://github.com/dotnet/arcade - 3ea0d860c6973f2cbadc9e895c7ec2cbdaec4ad5 + 98bacf3d68679c51adbb7c596a3de16a7f9acacf - + https://github.com/dotnet/arcade - 3ea0d860c6973f2cbadc9e895c7ec2cbdaec4ad5 + 98bacf3d68679c51adbb7c596a3de16a7f9acacf - + https://github.com/dotnet/arcade - 3ea0d860c6973f2cbadc9e895c7ec2cbdaec4ad5 + 98bacf3d68679c51adbb7c596a3de16a7f9acacf - + https://github.com/dotnet/arcade - 3ea0d860c6973f2cbadc9e895c7ec2cbdaec4ad5 + 98bacf3d68679c51adbb7c596a3de16a7f9acacf - + https://github.com/dotnet/arcade - 3ea0d860c6973f2cbadc9e895c7ec2cbdaec4ad5 + 98bacf3d68679c51adbb7c596a3de16a7f9acacf - + https://github.com/dotnet/arcade - 3ea0d860c6973f2cbadc9e895c7ec2cbdaec4ad5 + 98bacf3d68679c51adbb7c596a3de16a7f9acacf - + https://github.com/dotnet/arcade - 3ea0d860c6973f2cbadc9e895c7ec2cbdaec4ad5 + 98bacf3d68679c51adbb7c596a3de16a7f9acacf - + https://github.com/dotnet/arcade - 3ea0d860c6973f2cbadc9e895c7ec2cbdaec4ad5 + 98bacf3d68679c51adbb7c596a3de16a7f9acacf - + https://github.com/dotnet/arcade - 3ea0d860c6973f2cbadc9e895c7ec2cbdaec4ad5 + 98bacf3d68679c51adbb7c596a3de16a7f9acacf - + https://github.com/dotnet/arcade - 3ea0d860c6973f2cbadc9e895c7ec2cbdaec4ad5 + 98bacf3d68679c51adbb7c596a3de16a7f9acacf - + https://github.com/dotnet/arcade - 3ea0d860c6973f2cbadc9e895c7ec2cbdaec4ad5 + 98bacf3d68679c51adbb7c596a3de16a7f9acacf - + https://github.com/dotnet/arcade - 3ea0d860c6973f2cbadc9e895c7ec2cbdaec4ad5 + 98bacf3d68679c51adbb7c596a3de16a7f9acacf - + https://github.com/dotnet/arcade - 3ea0d860c6973f2cbadc9e895c7ec2cbdaec4ad5 + 98bacf3d68679c51adbb7c596a3de16a7f9acacf https://github.com/microsoft/vstest 140434f7109d357d0158ade9e5164a4861513965 - + https://github.com/dotnet/runtime-assets - 426f10a0dcb1a9b5fa3bf5901f88bd3a54ccaa0a + 256ee670d27044ab79756f3f955f7f1668f8000a - + https://github.com/dotnet/runtime-assets - 426f10a0dcb1a9b5fa3bf5901f88bd3a54ccaa0a + 256ee670d27044ab79756f3f955f7f1668f8000a - + https://github.com/dotnet/runtime-assets - 426f10a0dcb1a9b5fa3bf5901f88bd3a54ccaa0a + 256ee670d27044ab79756f3f955f7f1668f8000a - + https://github.com/dotnet/runtime-assets - 426f10a0dcb1a9b5fa3bf5901f88bd3a54ccaa0a + 256ee670d27044ab79756f3f955f7f1668f8000a - + https://github.com/dotnet/runtime-assets - 426f10a0dcb1a9b5fa3bf5901f88bd3a54ccaa0a + 256ee670d27044ab79756f3f955f7f1668f8000a - + https://github.com/dotnet/runtime-assets - 426f10a0dcb1a9b5fa3bf5901f88bd3a54ccaa0a + 256ee670d27044ab79756f3f955f7f1668f8000a - + https://github.com/dotnet/runtime-assets - 426f10a0dcb1a9b5fa3bf5901f88bd3a54ccaa0a + 256ee670d27044ab79756f3f955f7f1668f8000a - + https://github.com/dotnet/runtime-assets - 426f10a0dcb1a9b5fa3bf5901f88bd3a54ccaa0a + 256ee670d27044ab79756f3f955f7f1668f8000a - + https://github.com/dotnet/runtime-assets - 426f10a0dcb1a9b5fa3bf5901f88bd3a54ccaa0a + 256ee670d27044ab79756f3f955f7f1668f8000a - + https://github.com/dotnet/runtime-assets - 426f10a0dcb1a9b5fa3bf5901f88bd3a54ccaa0a + 256ee670d27044ab79756f3f955f7f1668f8000a https://github.com/dotnet/llvm-project @@ -166,81 +166,81 @@ https://github.com/dotnet/llvm-project fadaad69ceb75cd54372336dde2193a39348e740 - + https://github.com/dotnet/runtime - 78ac8430e8480742181170eef91a6fb016535aa4 + 03e90a540cb2dfe5cab4086ea54ad5dd1f655749 - + https://github.com/dotnet/runtime - 78ac8430e8480742181170eef91a6fb016535aa4 + 03e90a540cb2dfe5cab4086ea54ad5dd1f655749 - + https://github.com/dotnet/runtime - 78ac8430e8480742181170eef91a6fb016535aa4 + 03e90a540cb2dfe5cab4086ea54ad5dd1f655749 - + https://github.com/dotnet/runtime - 78ac8430e8480742181170eef91a6fb016535aa4 + 03e90a540cb2dfe5cab4086ea54ad5dd1f655749 - + https://github.com/dotnet/runtime - 78ac8430e8480742181170eef91a6fb016535aa4 + 03e90a540cb2dfe5cab4086ea54ad5dd1f655749 - + https://github.com/dotnet/runtime - 78ac8430e8480742181170eef91a6fb016535aa4 + 03e90a540cb2dfe5cab4086ea54ad5dd1f655749 - + https://github.com/dotnet/runtime - 78ac8430e8480742181170eef91a6fb016535aa4 + 03e90a540cb2dfe5cab4086ea54ad5dd1f655749 - + https://github.com/dotnet/runtime - 78ac8430e8480742181170eef91a6fb016535aa4 + 03e90a540cb2dfe5cab4086ea54ad5dd1f655749 https://github.com/dotnet/linker b0b20476551dacaf86946aeb957973a8540fb960 - + https://github.com/dotnet/xharness - 27779945a6f28639f526ec3d1e02f76436c6d22d + e7e0ec10c66f3c290e454a497a46cc41f8e59845 - + https://github.com/dotnet/xharness - 27779945a6f28639f526ec3d1e02f76436c6d22d + e7e0ec10c66f3c290e454a497a46cc41f8e59845 - + https://github.com/dotnet/arcade - 3ea0d860c6973f2cbadc9e895c7ec2cbdaec4ad5 + 98bacf3d68679c51adbb7c596a3de16a7f9acacf - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - deb41ed73af3390e435a68fe6b5671f37113d526 + 7ad072e2561e4052c9f0ff0830b43834f0b763d4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - deb41ed73af3390e435a68fe6b5671f37113d526 + 7ad072e2561e4052c9f0ff0830b43834f0b763d4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - deb41ed73af3390e435a68fe6b5671f37113d526 + 7ad072e2561e4052c9f0ff0830b43834f0b763d4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - deb41ed73af3390e435a68fe6b5671f37113d526 + 7ad072e2561e4052c9f0ff0830b43834f0b763d4 - + https://github.com/dotnet/hotreload-utils - fc3252e58fef2651652702298107f3fc3e3784cb + 19751dd4e49b0acafa676a43f08cfe9ab838a9cd - + https://github.com/dotnet/runtime-assets - 426f10a0dcb1a9b5fa3bf5901f88bd3a54ccaa0a + 256ee670d27044ab79756f3f955f7f1668f8000a - + https://github.com/dotnet/roslyn-analyzers - 5e80ab913df6662e4c94b6bdaa443684706f8e52 + ccd85bc350c9a994b74642f3b7613d8a98f5be2d https://github.com/dotnet/sdk diff --git a/eng/Versions.props b/eng/Versions.props index ca6b85449769e..9824bb7e0d0f2 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -50,36 +50,36 @@ 4.0.0-4.final 4.0.0-4.final - 7.0.0-preview1.21502.1 + 7.0.0-preview1.21506.6 4.0.0-5.21453.15 1.0.0-rc.2.21501.51 - 7.0.0-beta.21474.2 - 7.0.0-beta.21474.2 - 7.0.0-beta.21474.2 - 7.0.0-beta.21474.2 - 7.0.0-beta.21474.2 - 7.0.0-beta.21474.2 - 2.5.1-beta.21474.2 - 7.0.0-beta.21474.2 - 7.0.0-beta.21474.2 - 7.0.0-beta.21474.2 - 7.0.0-beta.21474.2 - 7.0.0-beta.21474.2 - 7.0.0-beta.21474.2 - 7.0.0-beta.21474.2 - 7.0.0-beta.21474.2 + 7.0.0-beta.21507.3 + 7.0.0-beta.21507.3 + 7.0.0-beta.21507.3 + 7.0.0-beta.21507.3 + 7.0.0-beta.21507.3 + 7.0.0-beta.21507.3 + 2.5.1-beta.21507.3 + 7.0.0-beta.21507.3 + 7.0.0-beta.21507.3 + 7.0.0-beta.21507.3 + 7.0.0-beta.21507.3 + 7.0.0-beta.21507.3 + 7.0.0-beta.21507.3 + 7.0.0-beta.21507.3 + 7.0.0-beta.21507.3 6.0.0-preview.1.102 - 7.0.0-alpha.1.21476.2 - 7.0.0-alpha.1.21476.2 - 7.0.0-alpha.1.21476.2 + 7.0.0-alpha.1.21501.7 + 7.0.0-alpha.1.21501.7 + 7.0.0-alpha.1.21501.7 3.1.0 - 7.0.0-alpha.1.21476.2 + 7.0.0-alpha.1.21501.7 5.0.0 4.3.0 @@ -113,28 +113,28 @@ 5.0.0 5.0.0 4.9.0-rc2.21473.1 - 7.0.0-alpha.1.21476.2 - 7.0.0-alpha.1.21476.2 + 7.0.0-alpha.1.21501.7 + 7.0.0-alpha.1.21501.7 4.5.4 4.5.0 - 7.0.0-alpha.1.21476.2 + 7.0.0-alpha.1.21501.7 - 7.0.0-beta.21479.3 - 7.0.0-beta.21479.3 - 7.0.0-beta.21479.3 - 7.0.0-beta.21479.3 - 7.0.0-beta.21479.3 - 7.0.0-beta.21479.3 - 7.0.0-beta.21479.3 - 7.0.0-beta.21479.3 - 7.0.0-beta.21479.3 - 7.0.0-beta.21479.3 - 7.0.0-beta.21479.3 + 7.0.0-beta.21506.1 + 7.0.0-beta.21506.1 + 7.0.0-beta.21506.1 + 7.0.0-beta.21506.1 + 7.0.0-beta.21506.1 + 7.0.0-beta.21506.1 + 7.0.0-beta.21506.1 + 7.0.0-beta.21506.1 + 7.0.0-beta.21506.1 + 7.0.0-beta.21506.1 + 7.0.0-beta.21506.1 - 1.0.0-prerelease.21502.2 - 1.0.0-prerelease.21502.2 - 1.0.0-prerelease.21502.2 - 1.0.0-prerelease.21502.2 + 1.0.0-prerelease.21507.4 + 1.0.0-prerelease.21507.4 + 1.0.0-prerelease.21507.4 + 1.0.0-prerelease.21507.4 16.9.0-beta1.21055.5 2.0.0-beta1.20253.1 @@ -158,9 +158,9 @@ 1.0.1-prerelease-00006 16.9.0-preview-20201201-01 - 1.0.0-prerelease.21501.2 - 1.0.0-prerelease.21501.2 - 1.0.2-alpha.0.21479.1 + 1.0.0-prerelease.21508.1 + 1.0.0-prerelease.21508.1 + 1.0.2-alpha.0.21504.1 2.4.2-pre.9 2.4.2 1.3.0 diff --git a/eng/common/native/init-compiler.sh b/eng/common/native/init-compiler.sh index 28f5145a6f794..8c944f30b2864 100644 --- a/eng/common/native/init-compiler.sh +++ b/eng/common/native/init-compiler.sh @@ -50,8 +50,8 @@ if [[ -z "$CLR_CC" ]]; then # Set default versions if [[ -z "$majorVersion" ]]; then # note: gcc (all versions) and clang versions higher than 6 do not have minor version in file name, if it is zero. - if [[ "$compiler" == "clang" ]]; then versions=( 12 11 10 9 8 7 6.0 5.0 4.0 3.9 3.8 3.7 3.6 3.5 ) - elif [[ "$compiler" == "gcc" ]]; then versions=( 11 10 9 8 7 6 5 4.9 ); fi + if [[ "$compiler" == "clang" ]]; then versions=( 13 12 11 10 9 8 7 6.0 5.0 4.0 3.9 3.8 3.7 3.6 3.5 ) + elif [[ "$compiler" == "gcc" ]]; then versions=( 12 11 10 9 8 7 6 5 4.9 ); fi for version in "${versions[@]}"; do parts=(${version//./ }) diff --git a/eng/common/templates/steps/send-to-helix.yml b/eng/common/templates/steps/send-to-helix.yml index cd02ae1607f3b..09a223989f7ec 100644 --- a/eng/common/templates/steps/send-to-helix.yml +++ b/eng/common/templates/steps/send-to-helix.yml @@ -20,7 +20,6 @@ parameters: IncludeDotNetCli: false # optional -- true will download a version of the .NET CLI onto the Helix machine as a correlation payload; requires DotNetCliPackageType and DotNetCliVersion DotNetCliPackageType: '' # optional -- either 'sdk', 'runtime' or 'aspnetcore-runtime'; determines whether the sdk or runtime will be sent to Helix; see https://raw.githubusercontent.com/dotnet/core/main/release-notes/releases-index.json DotNetCliVersion: '' # optional -- version of the CLI to send to Helix; based on this: https://raw.githubusercontent.com/dotnet/core/main/release-notes/releases-index.json - EnableXUnitReporter: false # optional -- true enables XUnit result reporting to Mission Control WaitForWorkItemCompletion: true # optional -- true will make the task wait until work items have been completed and fail the build if work items fail. False is "fire and forget." IsExternal: false # [DEPRECATED] -- doesn't do anything, jobs are external if HelixAccessToken is empty and Creator is set HelixBaseUri: 'https://helix.dot.net/' # optional -- sets the Helix API base URI (allows targeting int) @@ -54,7 +53,6 @@ steps: IncludeDotNetCli: ${{ parameters.IncludeDotNetCli }} DotNetCliPackageType: ${{ parameters.DotNetCliPackageType }} DotNetCliVersion: ${{ parameters.DotNetCliVersion }} - EnableXUnitReporter: ${{ parameters.EnableXUnitReporter }} WaitForWorkItemCompletion: ${{ parameters.WaitForWorkItemCompletion }} HelixBaseUri: ${{ parameters.HelixBaseUri }} Creator: ${{ parameters.Creator }} @@ -85,7 +83,6 @@ steps: IncludeDotNetCli: ${{ parameters.IncludeDotNetCli }} DotNetCliPackageType: ${{ parameters.DotNetCliPackageType }} DotNetCliVersion: ${{ parameters.DotNetCliVersion }} - EnableXUnitReporter: ${{ parameters.EnableXUnitReporter }} WaitForWorkItemCompletion: ${{ parameters.WaitForWorkItemCompletion }} HelixBaseUri: ${{ parameters.HelixBaseUri }} Creator: ${{ parameters.Creator }} diff --git a/global.json b/global.json index 6eac323f59052..df4e5c6ed44ba 100644 --- a/global.json +++ b/global.json @@ -12,12 +12,12 @@ "python3": "3.7.1" }, "msbuild-sdks": { - "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "7.0.0-beta.21474.2", - "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.21474.2", - "Microsoft.DotNet.Helix.Sdk": "7.0.0-beta.21474.2", - "Microsoft.DotNet.SharedFramework.Sdk": "7.0.0-beta.21474.2", + "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "7.0.0-beta.21507.3", + "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.21507.3", + "Microsoft.DotNet.Helix.Sdk": "7.0.0-beta.21507.3", + "Microsoft.DotNet.SharedFramework.Sdk": "7.0.0-beta.21507.3", "Microsoft.Build.NoTargets": "3.1.0", "Microsoft.Build.Traversal": "3.0.23", - "Microsoft.NET.Sdk.IL": "7.0.0-alpha.1.21476.2" + "Microsoft.NET.Sdk.IL": "7.0.0-alpha.1.21501.7" } } From 6f1e649aee9ccaff5f8c12fcbbc2f219d3c4e8eb Mon Sep 17 00:00:00 2001 From: Andrew Au Date: Fri, 8 Oct 2021 08:52:44 -0700 Subject: [PATCH 010/106] Fix gc.cpp compilation with BACKGROUND_GC disabled (#59958) --- src/coreclr/gc/gc.cpp | 121 +++++++++++++++++++++++++++++----------- src/coreclr/gc/gcpriv.h | 25 +++++---- 2 files changed, 102 insertions(+), 44 deletions(-) diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index c73321be10a4f..2ae2be2d8a6a4 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -24,6 +24,10 @@ #define USE_INTROSORT #endif +#ifdef DACCESS_COMPILE +#error this source file should not be compiled with DACCESS_COMPILE! +#endif //DACCESS_COMPILE + // We just needed a simple random number generator for testing. class gc_rand { @@ -398,6 +402,7 @@ VOLATILE(bgc_state) gc_heap::current_bgc_state = bgc_not_in_process; int gc_heap::gchist_index_per_heap = 0; gc_heap::gc_history gc_heap::gchist_per_heap[max_history_count]; #endif //MULTIPLE_HEAPS +#endif //BACKGROUND_GC void gc_heap::add_to_history_per_heap() { @@ -444,8 +449,6 @@ void gc_heap::add_to_history() #endif //GC_HISTORY && BACKGROUND_GC } -#endif //BACKGROUND_GC - #ifdef TRACE_GC BOOL gc_log_on = TRUE; FILE* gc_log = NULL; @@ -1242,7 +1245,6 @@ class exclusive_sync void uoh_alloc_done (uint8_t* obj) { -#ifdef BACKGROUND_GC if (!gc_heap::cm_in_progress) { return; @@ -1256,7 +1258,6 @@ class exclusive_sync return; } } -#endif //BACKGROUND_GC } }; @@ -2973,7 +2974,11 @@ void gc_heap::fire_pevents() uint32_t count_time_info = (settings.concurrent ? max_bgc_time_type : (settings.compaction ? max_compact_time_type : max_sweep_time_type)); +#ifdef BACKGROUND_GC uint64_t* time_info = (settings.concurrent ? bgc_time_info : gc_time_info); +#else + uint64_t* time_info = gc_time_info; +#endif //BACKGROUND_GC // We don't want to have to fire the time info as 64-bit integers as there's no need to // so compress them down to 32-bit ones. uint32_t* time_info_32 = (uint32_t*)time_info; @@ -8369,13 +8374,8 @@ void gc_heap::clear_mark_array (uint8_t* from, uint8_t* end, BOOL check_only/*=T } assert (end == align_on_mark_word (end)); -#ifdef BACKGROUND_GC uint8_t* current_lowest_address = background_saved_lowest_address; uint8_t* current_highest_address = background_saved_highest_address; -#else - uint8_t* current_lowest_address = lowest_address; - uint8_t* current_highest_address = highest_address; -#endif //BACKGROUND_GC //there is a possibility of the addresses to be //outside of the covered range because of a newly allocated @@ -8518,12 +8518,12 @@ void gc_heap::get_card_table_element_sizes (uint8_t* start, uint8_t* end, size_t sizes[card_bundle_table_element] = size_card_bundle_of (start, end); } #endif //CARD_BUNDLE -#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP +#if defined(FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP) && defined (BACKGROUND_GC) if (gc_can_use_concurrent) { sizes[software_write_watch_table_element] = SoftwareWriteWatch::GetTableByteSize(start, end); } -#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP +#endif //FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP && BACKGROUND_GC sizes[seg_mapping_table_element] = size_seg_mapping_table_of (start, end); #ifdef BACKGROUND_GC if (gc_can_use_concurrent) @@ -8819,12 +8819,12 @@ uint32_t* gc_heap::make_card_table (uint8_t* start, uint8_t* end) #endif #endif //CARD_BUNDLE -#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP +#if defined(FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP) && defined (BACKGROUND_GC) if (gc_can_use_concurrent) { SoftwareWriteWatch::InitializeUntranslatedTable(mem + card_table_element_layout[software_write_watch_table_element], start); } -#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP +#endif //FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP && BACKGROUND_GC seg_mapping_table = (seg_mapping*)(mem + card_table_element_layout[seg_mapping_table_element]); seg_mapping_table = (seg_mapping*)((uint8_t*)seg_mapping_table - @@ -9047,7 +9047,7 @@ int gc_heap::grow_brick_card_tables (uint8_t* start, } #endif //BACKGROUND_GC -#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP +#if defined(FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP) && defined(BACKGROUND_GC) if (gc_can_use_concurrent) { // The current design of software write watch requires that the runtime is suspended during resize. Suspending @@ -9097,7 +9097,7 @@ int gc_heap::grow_brick_card_tables (uint8_t* start, } } else -#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP +#endif //FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP && BACKGROUND_GC { g_gc_card_table = translated_ct; @@ -9182,6 +9182,7 @@ void gc_heap::copy_brick_card_range (uint8_t* la, uint32_t* old_card_table, uint32_t* old_ct = &old_card_table[card_word (card_of (la))]; +#ifdef BACKGROUND_GC if (gc_heap::background_running_p()) { uint32_t* old_mark_array = card_table_mark_array (old_ct); @@ -9210,6 +9211,7 @@ void gc_heap::copy_brick_card_range (uint8_t* la, uint32_t* old_card_table, assert (old_brick_table == 0); } } +#endif //BACKGROUND_GC // n way merge with all of the card table ever used in between uint32_t* ct = card_table_next (&card_table[card_word (card_of(lowest_address))]); @@ -9349,7 +9351,10 @@ BOOL gc_heap::insert_ro_segment (heap_segment* seg) enter_spin_lock (&gc_heap::gc_lock); if (!gc_heap::seg_table->ensure_space_for_insert () - || (is_bgc_in_progress() && !commit_mark_array_new_seg(__this, seg))) +#ifdef BACKGROUND_GC + || (is_bgc_in_progress() && !commit_mark_array_new_seg(__this, seg)) +#endif //BACKGROUND_GC + ) { leave_spin_lock(&gc_heap::gc_lock); return FALSE; @@ -12158,7 +12163,10 @@ void gc_heap::distribute_free_regions() // if so, put the highest free regions on the decommit list total_num_free_regions[kind] += num_regions_to_decommit[kind]; - if (background_running_p() || + if ( +#ifdef BACKGROUND_GC + background_running_p() || +#endif ((total_num_free_regions[kind] + num_huge_region_units_to_consider[kind]) < total_budget_in_region_units[kind])) { dprintf (REGIONS_LOG, ("distributing the %Id %s regions deficit", @@ -12386,6 +12394,7 @@ void gc_heap::update_card_table_bundle() } #endif //CARD_BUNDLE +#ifdef BACKGROUND_GC // static void gc_heap::reset_write_watch_for_gc_heap(void* base_address, size_t region_size) { @@ -12506,6 +12515,7 @@ void gc_heap::reset_write_watch (BOOL concurrent_p) } } } +#endif //BACKGROUND_GC #endif //WRITE_WATCH @@ -12924,9 +12934,11 @@ HRESULT gc_heap::initialize_gc (size_t soh_segment_size, if (!g_promoted) return E_OUTOFMEMORY; #endif //!USE_REGIONS || _DEBUG +#ifdef BACKGROUND_GC g_bpromoted = new (nothrow) size_t [number_of_heaps*16]; if (!g_bpromoted) return E_OUTOFMEMORY; +#endif #ifdef MH_SC_MARK g_mark_stack_busy = new (nothrow) int[(number_of_heaps+2)*HS_CACHE_LINE_SIZE/sizeof(int)]; @@ -27284,11 +27296,13 @@ uint8_t* gc_heap::find_next_marked (uint8_t* x, uint8_t* end, #endif //MULTIPLE_HEAPS ) x = *mark_list_next; +#ifdef BACKGROUND_GC if (current_c_gc_state == c_gc_state_marking) { assert(gc_heap::background_running_p()); bgc_clear_batch_mark_array_bits (old_x, x); } +#endif //BACKGROUND_GC } else { @@ -28615,8 +28629,11 @@ void gc_heap::plan_phase (int condemned_gen_number) } } - if (maxgen_size_inc_p && provisional_mode_triggered && - !is_bgc_in_progress()) + if (maxgen_size_inc_p && provisional_mode_triggered +#ifdef BACKGROUND_GC + && !is_bgc_in_progress() +#endif //BACKGROUND_GC + ) { pm_trigger_full_gc = true; dprintf (GTC_LOG, ("in PM: maxgen size inc, doing a sweeping gen1 and trigger NGC2")); @@ -28741,8 +28758,11 @@ void gc_heap::plan_phase (int condemned_gen_number) rearrange_uoh_segments (); } - if (maxgen_size_inc_p && provisional_mode_triggered && - !is_bgc_in_progress()) + if (maxgen_size_inc_p && provisional_mode_triggered +#ifdef BACKGROUND_GC + && !is_bgc_in_progress() +#endif //BACKGROUND_GC + ) { pm_trigger_full_gc = true; dprintf (GTC_LOG, ("in PM: maxgen size inc, doing a sweeping gen1 and trigger NGC2")); @@ -28790,8 +28810,11 @@ void gc_heap::plan_phase (int condemned_gen_number) if (!pm_trigger_full_gc && pm_stress_on && provisional_mode_triggered) { if ((settings.condemned_generation == (max_generation - 1)) && - ((settings.gc_index % 5) == 0) && - !is_bgc_in_progress()) + ((settings.gc_index % 5) == 0) +#ifdef BACKGROUND_GC + && !is_bgc_in_progress() +#endif //BACKGROUND_GC + ) { pm_trigger_full_gc = true; } @@ -38061,9 +38084,13 @@ void gc_heap::init_static_data() #ifdef MULTIPLE_HEAPS max (6*1024*1024, min ( Align(soh_segment_size/2), 200*1024*1024)); #else //MULTIPLE_HEAPS - (gc_can_use_concurrent ? + ( +#ifdef BACKGROUND_GC + gc_can_use_concurrent ? 6*1024*1024 : - max (6*1024*1024, min ( Align(soh_segment_size/2), 200*1024*1024))); +#endif //BACKGROUND_GC + max (6*1024*1024, min ( Align(soh_segment_size/2), 200*1024*1024)) + ); #endif //MULTIPLE_HEAPS gen0_max_size = max (gen0_min_size, gen0_max_size); @@ -38094,9 +38121,13 @@ void gc_heap::init_static_data() #ifdef MULTIPLE_HEAPS max (6*1024*1024, Align(soh_segment_size/2)); #else //MULTIPLE_HEAPS - (gc_can_use_concurrent ? + ( +#ifdef BACKGROUND_GC + gc_can_use_concurrent ? 6*1024*1024 : - max (6*1024*1024, Align(soh_segment_size/2))); +#endif //BACKGROUND_GC + max (6*1024*1024, Align(soh_segment_size/2)) + ); #endif //MULTIPLE_HEAPS size_t gen1_max_size_config = (size_t)GCConfig::GetGCGen1MaxBudget(); @@ -44685,12 +44716,14 @@ void gc_heap::do_post_gc() // Now record the gc info. last_recorded_gc_info* last_gc_info = 0; +#ifdef BACKGROUND_GC if (settings.concurrent) { last_gc_info = &last_bgc_info[last_bgc_info_index]; assert (last_gc_info->index == settings.gc_index); } else +#endif //BACKGROUND_GC { last_gc_info = ((settings.condemned_generation == max_generation) ? &last_full_blocking_gc_info : &last_ephemeral_gc_info); @@ -44710,10 +44743,12 @@ void gc_heap::do_post_gc() uint64_t gc_start_ts = dd_time_clock (dd); size_t pause_duration = (size_t)(end_gc_time - dd_time_clock (dd)); +#ifdef BACKGROUND_GC if ((hp->current_bgc_state != bgc_initialized) && (settings.reason != reason_pm_full_gc)) { pause_duration += (size_t)(gc_start_ts - suspended_start_time); } +#endif //BACKGROUND_GC last_gc_info->pause_durations[0] = pause_duration; total_suspended_time += pause_duration; @@ -45072,9 +45107,15 @@ size_t GCHeap::ApproxTotalBytesInUse(BOOL small_heap_only) // Get small block heap size info totsize = (pGenGCHeap->alloc_allocated - heap_segment_mem (eph_seg)); heap_segment* seg1 = generation_start_segment (pGenGCHeap->generation_of (max_generation)); - while ((seg1 != eph_seg) && (seg1 != nullptr) && (seg1 != pGenGCHeap->freeable_soh_segment)) + while ((seg1 != eph_seg) && (seg1 != nullptr) +#ifdef BACKGROUND_GC + && (seg1 != pGenGCHeap->freeable_soh_segment) +#endif //BACKGROUND_GC + ) { +#ifdef BACKGROUND_GC if (!heap_segment_decommitted_p (seg1)) +#endif //BACKGROUND_GC { totsize += heap_segment_allocated (seg1) - heap_segment_mem (seg1); @@ -46349,7 +46390,11 @@ void GCHeap::DiagGetGCSettings(EtwGCSettingsInfo* etw_settings) etw_settings->gen0_min_budget_from_config = gc_heap::gen0_min_budget_from_config; etw_settings->gen0_max_budget_from_config = gc_heap::gen0_max_budget_from_config; etw_settings->high_mem_percent_from_config = gc_heap::high_mem_percent_from_config; +#ifdef BACKGROUND_GC etw_settings->concurrent_gc_p = gc_heap::gc_can_use_concurrent; +#else + etw_settings->concurrent_gc_p = false; +#endif //BACKGROUND_GC etw_settings->use_large_pages_p = gc_heap::use_large_pages_p; etw_settings->use_frozen_segments_p = gc_heap::use_frozen_segments_p; etw_settings->hard_limit_config_p = gc_heap::hard_limit_config_p; @@ -46662,10 +46707,14 @@ void PopulateDacVars(GcDacVars *gcDacVars) gcDacVars->generation_size = sizeof(generation); gcDacVars->total_generation_count = total_generation_count; gcDacVars->max_gen = &g_max_generation; - gcDacVars->current_c_gc_state = const_cast(&gc_heap::current_c_gc_state); #ifndef MULTIPLE_HEAPS - gcDacVars->mark_array = &gc_heap::mark_array; gcDacVars->ephemeral_heap_segment = reinterpret_cast(&gc_heap::ephemeral_heap_segment); +#ifdef BACKGROUND_GC + gcDacVars->mark_array = &gc_heap::mark_array; + gcDacVars->current_c_gc_state = const_cast(&gc_heap::current_c_gc_state); + gcDacVars->background_saved_lowest_address = &gc_heap::background_saved_lowest_address; + gcDacVars->background_saved_highest_address = &gc_heap::background_saved_highest_address; + gcDacVars->next_sweep_obj = &gc_heap::next_sweep_obj; #ifdef USE_REGIONS gcDacVars->saved_sweep_ephemeral_seg = 0; gcDacVars->saved_sweep_ephemeral_start = 0; @@ -46673,10 +46722,16 @@ void PopulateDacVars(GcDacVars *gcDacVars) gcDacVars->saved_sweep_ephemeral_seg = reinterpret_cast(&gc_heap::saved_sweep_ephemeral_seg); gcDacVars->saved_sweep_ephemeral_start = &gc_heap::saved_sweep_ephemeral_start; #endif //USE_REGIONS - gcDacVars->background_saved_lowest_address = &gc_heap::background_saved_lowest_address; - gcDacVars->background_saved_highest_address = &gc_heap::background_saved_highest_address; +#else //BACKGROUND_GC + gcDacVars->mark_array = 0; + gcDacVars->current_c_gc_state = 0; + gcDacVars->background_saved_lowest_address = 0; + gcDacVars->background_saved_highest_address = 0; + gcDacVars->next_sweep_obj = 0; + gcDacVars->saved_sweep_ephemeral_seg = 0; + gcDacVars->saved_sweep_ephemeral_start = 0; +#endif //BACKGROUND_GC gcDacVars->alloc_allocated = &gc_heap::alloc_allocated; - gcDacVars->next_sweep_obj = &gc_heap::next_sweep_obj; gcDacVars->oom_info = &gc_heap::oom_info; gcDacVars->finalize_queue = reinterpret_cast(&gc_heap::finalize_queue); gcDacVars->generation_table = reinterpret_cast(&gc_heap::generation_table); diff --git a/src/coreclr/gc/gcpriv.h b/src/coreclr/gc/gcpriv.h index 1fd5f0421daa8..051dd99301490 100644 --- a/src/coreclr/gc/gcpriv.h +++ b/src/coreclr/gc/gcpriv.h @@ -85,8 +85,10 @@ inline void FATAL_GC_ERROR() #define FEATURE_PREMORTEM_FINALIZATION #define GC_HISTORY +#define BACKGROUND_GC //concurrent background GC (requires WRITE_WATCH) + // We need the lower 3 bits in the MT to do our bookkeeping so doubly linked free list is only for 64-bit -#ifdef HOST_64BIT +#if defined(BACKGROUND_GC) && defined(HOST_64BIT) #define DOUBLY_LINKED_FL #endif //HOST_64BIT @@ -99,8 +101,6 @@ inline void FATAL_GC_ERROR() #define initial_internal_roots (1024*16) #endif // HEAP_ANALYZE -#define BACKGROUND_GC //concurrent background GC (requires WRITE_WATCH) - #ifdef SERVER_GC #define MH_SC_MARK //scalable marking //#define SNOOP_STATS //diagnostic @@ -2038,11 +2038,11 @@ class gc_heap #endif //!USE_REGIONS PER_HEAP_ISOLATED void distribute_free_regions(); +#ifdef BACKGROUND_GC PER_HEAP_ISOLATED void reset_write_watch_for_gc_heap(void* base_address, size_t region_size); PER_HEAP_ISOLATED void get_write_watch_for_gc_heap(bool reset, void *base_address, size_t region_size, void** dirty_pages, uintptr_t* dirty_page_count_ref, bool is_runtime_suspended); - PER_HEAP void switch_one_quantum(); PER_HEAP @@ -2051,6 +2051,7 @@ class gc_heap void switch_on_reset (BOOL concurrent_p, size_t* current_total_reset_size, size_t last_reset_size); PER_HEAP void reset_write_watch (BOOL concurrent_p); +#endif //BACKGROUND_GC PER_HEAP void adjust_ephemeral_limits(); PER_HEAP @@ -5918,14 +5919,7 @@ inline gc_oh_num heap_segment_oh (heap_segment * inst) } } -#ifdef BACKGROUND_GC #ifdef USE_REGIONS -inline -bool heap_segment_overflow_p (heap_segment* inst) -{ - return ((inst->flags & heap_segment_flags_overflow) != 0); -} - inline region_free_list*& heap_segment_containing_free_list (heap_segment* inst) { @@ -5939,6 +5933,15 @@ PTR_heap_segment& heap_segment_prev_free_region (heap_segment* inst) } #endif //USE_REGIONS +#ifdef BACKGROUND_GC +#ifdef USE_REGIONS +inline +bool heap_segment_overflow_p (heap_segment* inst) +{ + return ((inst->flags & heap_segment_flags_overflow) != 0); +} +#endif //USE_REGIONS + inline BOOL heap_segment_decommitted_p (heap_segment * inst) { From 6732e2ee117e01f29ed81ad293983f9ca602ef37 Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Fri, 8 Oct 2021 17:16:41 +0100 Subject: [PATCH 011/106] Add issue reporting guidelines to CONTRIBUTING.md (#60142) * Add issue reporting guidelines to CONTRIBUTING.md * address feeedback * update README.md * link bug report template to new CONTRIBUTING.md section --- .github/ISSUE_TEMPLATE/01_bug_report.yml | 2 +- CONTRIBUTING.md | 84 +++++++++++++++++++----- README.md | 2 +- 3 files changed, 71 insertions(+), 17 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/01_bug_report.yml b/.github/ISSUE_TEMPLATE/01_bug_report.yml index ee28f6b233a15..4aa6545ec43bd 100644 --- a/.github/ISSUE_TEMPLATE/01_bug_report.yml +++ b/.github/ISSUE_TEMPLATE/01_bug_report.yml @@ -5,7 +5,7 @@ body: - type: markdown attributes: value: | - We welcome bug reports! Please see our [contribution guidelines](https://github.com/dotnet/runtime/blob/main/CONTRIBUTING.md) for more information on writing a good bug report. This template will help us gather the information we need to start the triage process. + We welcome bug reports! Please see our [contribution guidelines](https://github.com/dotnet/runtime/blob/main/CONTRIBUTING.md#writing-a-good-bug-report) for more information on writing a good bug report. This template will help us gather the information we need to start the triage process. - type: textarea id: background attributes: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0b5c363bdc32c..9b87e2ce453d9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,9 +1,63 @@ -Contribution to .NET Runtime -===================== +# Contribution to .NET Runtime You can contribute to .NET Runtime with issues and PRs. Simply filing issues for problems you encounter is a great way to contribute. Contributing implementations is greatly appreciated. -## Contribution "Bar" +## Reporting Issues + +We always welcome bug reports, API proposals and overall feedback. Here are a few tips on how you can make reporting your issue as effective as possible. + +### Identify Where to Report + +The .NET codebase is distributed across multiple repositories in the [dotnet organization](https://github.com/dotnet). Depending on the feedback you might want to file the issue on a different repo. Here are a few common repos: + +* [dotnet/runtime](https://github.com/dotnet/runtime) .NET runtime, libraries and shared host installers. +* [dotnet/roslyn](https://github.com/dotnet/roslyn) C# and VB compiler. +* [dotnet/aspnetcore](https://github.com/dotnet/aspnetcore) ASP.NET Core. +* [dotnet/core](https://github.com/dotnet/core) Can be used to submit feedback if not sure what repo to use. + +### Finding Existing Issues + +Before filing a new issue, please search our [open issues](https://github.com/dotnet/runtime/issues) to check if it already exists. + +If you do find an existing issue, please include your own feedback in the discussion. Do consider upvoting (👍 reaction) the original post, as this helps us prioritize popular issues in our backlog. + +### Writing a Good API Proposal + +Please review our [API review process](https://github.com/dotnet/runtime/blob/main/docs/project/api-review-process.md) documents for guidelines on how to submit an API review. When ready to submit a proposal, please use the [API Suggestion issue template](https://github.com/dotnet/runtime/issues/new?assignees=&labels=api-suggestion&template=02_api_proposal.yml&title=%5BAPI+Proposal%5D%3A+). + +### Writing a Good Bug Report + +Good bug reports make it easier for maintainers to verify and root cause the underlying problem. The better a bug report, the faster the problem will be resolved. Ideally, a bug report should contain the following information: + +* A high-level description of the problem. +* A _minimal reproduction_, i.e. the smallest size of code/configuration required to reproduce the wrong behavior. +* A description of the _expected behavior_, contrasted with the _actual behavior_ observed. +* Information on the environment: OS/distro, CPU arch, SDK version, etc. +* Additional information, e.g. is it a regression from previous versions? are there any known workarounds? + +When ready to submit a bug report, please use the [Bug Report issue template](https://github.com/dotnet/runtime/issues/new?assignees=&labels=&template=01_bug_report.yml). + +#### Why are Minimal Reproductions Important? + +A reproduction lets maintainers verify the presence of a bug, and diagnose the issue using a debugger. A _minimal_ reproduction is the smallest possible console application demonstrating that bug. Minimal reproductions are generally preferable since they: + +1. Focus debugging efforts on a simple code snippet, +2. Ensure that the problem is not caused by unrelated dependencies/configuration, +3. Avoid the need to share production codebases. + +#### Are Minimal Reproductions Required? + +In certain cases, creating a minimal reproduction might not be practical (e.g. due to nondeterministic factors, external dependencies). In such cases you would be asked to provide as much information as possible, for example by sharing a memory dump of the failing application. If maintainers are unable to root cause the problem, they might still close the issue as not actionable. While not required, minimal reproductions are strongly encouraged and will significantly improve the chances of your issue being prioritized and fixed by the maintainers. + +#### How to Create a Minimal Reproduction + +The best way to create a minimal reproduction is gradually removing code and dependencies from a reproducing app, until the problem no longer occurs. A good minimal reproduction: + +* Excludes all unnecessary types, methods, code blocks, source files, nuget dependencies and project configurations. +* Contains documentation or code comments illustrating expected vs actual behavior. +* If possible, avoids performing any unneeded IO or system calls. For example, can the ASP.NET based reproduction be converted to a plain old console app? + +## Contributing Changes Project maintainers will merge changes that improve the product significantly and broadly align with the [.NET Roadmap](https://github.com/dotnet/core/blob/master/roadmap.md). @@ -11,7 +65,7 @@ Maintainers will not merge changes that have narrowly-defined benefits, due to c Contributions must also satisfy the other published guidelines defined in this document. -## DOs and DON'Ts +### DOs and DON'Ts Please do: @@ -33,11 +87,11 @@ Please do not: * **DON'T** submit PRs that alter licensing related files or headers. If you believe there's a problem with them, file an issue and we'll be happy to discuss it. * **DON'T** add API additions without filing an issue and discussing with us first. See [API Review Process](docs/project/api-review-process.md). -## Breaking Changes +### Breaking Changes Contributions must maintain [API signature](docs/coding-guidelines/breaking-changes.md#bucket-1-public-contract) and behavioral compatibility. Contributions that include [breaking changes](docs/coding-guidelines/breaking-changes.md) will be rejected. Please file an issue to discuss your idea or change if you believe that it may affect managed code compatibility. -## Suggested Workflow +### Suggested Workflow We use and recommend the following workflow: @@ -67,11 +121,11 @@ We use and recommend the following workflow: - The next official build will automatically include your change. - You can delete the branch you used for making the change. -## Up for Grabs +### Up for Grabs The team marks the most straightforward issues as [up for grabs](https://github.com/dotnet/runtime/labels/up-for-grabs). This set of issues is the place to start if you are interested in contributing but new to the codebase. -## Commit Messages +### Commit Messages Please format commit messages as follows (based on [A Note About Git Commit Messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)): @@ -90,7 +144,7 @@ Fix #42 Also do your best to factor commits appropriately, not too large with unrelated things in the same commit, and not too small with the same small change applied N times in N different commits. -## Contributor License Agreement +### Contributor License Agreement You must sign a [.NET Foundation Contribution License Agreement (CLA)](https://cla.dotnetfoundation.org) before your PR will be merged. This is a one-time requirement for projects in the .NET Foundation. You can read more about [Contribution License Agreements (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) on Wikipedia. @@ -98,7 +152,7 @@ The agreement: [net-foundation-contribution-license-agreement.pdf](https://githu You don't have to do this up-front. You can simply clone, fork, and submit your pull-request as usual. When your pull-request is created, it is classified by a CLA bot. If the change is trivial (for example, you just fixed a typo), then the PR is labelled with `cla-not-required`. Otherwise it's classified as `cla-required`. Once you signed a CLA, the current and all future pull-requests will be labelled as `cla-signed`. -## File Headers +### File Headers The following file header is the used for .NET Core. Please use it for new files. @@ -110,13 +164,13 @@ The following file header is the used for .NET Core. Please use it for new files - See [class.cpp](./src/coreclr/vm/class.cpp) for an example of the header in a C++ file. - See [List.cs](./src/libraries/System.Private.CoreLib/src/System/Collections/Generic/List.cs) for an example of the header in a C# file. -## PR - CI Process +### PR - CI Process The [dotnet continuous integration](https://dev.azure.com/dnceng/public/) (CI) system will automatically perform the required builds and run tests (including the ones you are expected to run) for PRs. Builds and test runs must be clean. If the CI build fails for any reason, the PR issue will be updated with a link that can be used to determine the cause of the failure. -## PR Feedback +### PR Feedback Microsoft team and community members will provide feedback on your change. Community feedback is highly valued. You will often see the absence of team feedback if the community has already provided good review feedback. @@ -124,7 +178,7 @@ One or more Microsoft team members will review every PR prior to merge. They wil There are lots of thoughts and [approaches](https://github.com/antlr/antlr4-cpp/blob/master/CONTRIBUTING.md#emoji) for how to efficiently discuss changes. It is best to be clear and explicit with your feedback. Please be patient with people who might not understand the finer details about your approach to feedback. -## Contributing Ports +### Contributing Ports We encourage ports of CoreCLR to other platforms. There are multiple ports ongoing at any one time. You may be interested in one of the following ports: @@ -145,7 +199,7 @@ Note: Add links to install instructions for each of these ports. Ports have a weaker contribution bar, at least initially. A functionally correct implementation is considered an important first goal. Performance, reliability and compatibility are all important concerns after that. -### Copying Files from Other Projects +#### Copying Files from Other Projects .NET Core uses some files from other projects, typically where a binary distribution does not exist or would be inconvenient. @@ -157,7 +211,7 @@ The following rules must be followed for PRs that include files from another pro See [IdnMapping.cs](./src/libraries/System.Private.CoreLib/src/System/Globalization/IdnMapping.cs) for an example of a file copied from another project and attributed in the [CoreCLR 3rd party notices](./THIRD-PARTY-NOTICES.TXT) file. -### Porting Files from Other Projects +#### Porting Files from Other Projects There are many good algorithms implemented in other languages that would benefit the .NET Core project. The rules for porting a Java file to C#, for example, are the same as would be used for copying the same file, as described above. diff --git a/README.md b/README.md index 1444df567a8e1..350b72cc5b2c9 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Official Starting Page: https://dotnet.microsoft.com/ We welcome contributions! Many people all over the world have helped make this project better. -* [Contributing](CONTRIBUTING.md) explains what kinds of changes we welcome +* [Contributing](CONTRIBUTING.md) explains what kinds of contributions we welcome - [Workflow Instructions](docs/workflow/README.md) explains how to build and test * [Get Up and Running on .NET Core](docs/project/dogfooding.md) explains how to get nightly builds of the runtime and its libraries to test them in your own projects. From cf2157dbe78fc6b69f7894b3b64c89c7befebf28 Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Fri, 8 Oct 2021 09:35:48 -0700 Subject: [PATCH 012/106] Add support for cloning loops with `<=` conditions (#60148) * Add support for cloning loops with `<=` conditions Loop cloning currently only handles loops using `<` limit comparison, e.g., in loops like `for (i = c; i < n; i++)`. This change allows for loop cloning for loops using `<=`. This happens frequently in the BenchF benchmarks (which maybe had their origin long ago in 1-based array Fortran code?), and some in the BenchI benchmarks. In real world code, it happens in Visual Basic code. There are perf wins and losses, with the usual existing issues for loop cloning. For one case, BenchI.XPosMatrix, I see a loss due to various things, including: 1. Unfortunate register allocation and/or missing copy prop, leading to lots of unnecessary copies. 2. Hitting jcc erratum 3. CSE leads to un-contained array length load in array bounds check Even so, there is one fewer bounds check than the baseline. We could remove one more, but a limitation in recognizing multi-dimensional jagged array components prevents loop cloning from recognizing a legal-to-remove bounds check (https://github.com/dotnet/runtime/issues/54074). * Handle aborting from loop cloning for statically false conditions This change activated existing unused code that statically determined a loop choice condition was false, leading to aborting the loop cloning (and at runtime to an almost certain exception). (The test case was `for (i = 1; i <= a.Length; i++) { a[i] = 1; }`) If any choice condition is false, we shouldn't also check if all conditions are true (that value is unreliable in this case). There are no SPMI asm diffs from this change. --- src/coreclr/jit/loopcloning.cpp | 41 ++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/src/coreclr/jit/loopcloning.cpp b/src/coreclr/jit/loopcloning.cpp index 06c408395087f..83b9fe3aa01eb 100644 --- a/src/coreclr/jit/loopcloning.cpp +++ b/src/coreclr/jit/loopcloning.cpp @@ -534,7 +534,6 @@ void LoopCloneContext::EvaluateConditions(unsigned loopNum, bool* pAllTrue, bool // Since this will force us to abort loop cloning, there is no need compute an accurate `allTrue`, // so we can break out of the loop now. - // REVIEW: it appears we never hit this condition in any test. break; } } @@ -1009,7 +1008,7 @@ LC_Deref* LC_Deref::Find(JitExpandArrayStack* children, unsigned lcl) // Operation: // Inspect the loop cloning optimization candidates and populate the conditions necessary // for each optimization candidate. Checks if the loop stride is "> 0" if the loop -// condition is "less than". If the initializer is "var" init then adds condition +// condition is `<` or `<=`. If the initializer is "var" init then adds condition // "var >= 0", and if the loop is var limit then, "var >= 0" and "var <= a.len" // are added to "context". These conditions are checked in the pre-header block // and the cloning choice is made. @@ -1026,7 +1025,7 @@ bool Compiler::optDeriveLoopCloningConditions(unsigned loopNum, LoopCloneContext LoopDsc* loop = &optLoopTable[loopNum]; JitExpandArrayStack* optInfos = context->GetLoopOptInfo(loopNum); - if (loop->lpTestOper() == GT_LT) + if (GenTree::StaticOperIs(loop->lpTestOper(), GT_LT, GT_LE)) { // Stride conditions if (loop->lpIterConst() <= 0) @@ -1112,6 +1111,21 @@ bool Compiler::optDeriveLoopCloningConditions(unsigned loopNum, LoopCloneContext return false; } + // GT_LT loop test: limit <= arrLen + // GT_LE loop test: limit < arrLen + genTreeOps opLimitCondition; + switch (loop->lpTestOper()) + { + case GT_LT: + opLimitCondition = GT_LE; + break; + case GT_LE: + opLimitCondition = GT_LT; + break; + default: + unreached(); + } + for (unsigned i = 0; i < optInfos->Size(); ++i) { LcOptInfo* optInfo = optInfos->Get(i); @@ -1119,11 +1133,10 @@ bool Compiler::optDeriveLoopCloningConditions(unsigned loopNum, LoopCloneContext { case LcOptInfo::LcJaggedArray: { - // limit <= arrLen LcJaggedArrayOptInfo* arrIndexInfo = optInfo->AsLcJaggedArrayOptInfo(); LC_Array arrLen(LC_Array::Jagged, &arrIndexInfo->arrIndex, arrIndexInfo->dim, LC_Array::ArrLen); LC_Ident arrLenIdent = LC_Ident(arrLen); - LC_Condition cond(GT_LE, LC_Expr(ident), LC_Expr(arrLenIdent)); + LC_Condition cond(opLimitCondition, LC_Expr(ident), LC_Expr(arrLenIdent)); context->EnsureConditions(loopNum)->Push(cond); // Ensure that this array must be dereference-able, before executing the actual condition. @@ -1133,13 +1146,15 @@ bool Compiler::optDeriveLoopCloningConditions(unsigned loopNum, LoopCloneContext break; case LcOptInfo::LcMdArray: { - // limit <= mdArrLen LcMdArrayOptInfo* mdArrInfo = optInfo->AsLcMdArrayOptInfo(); - LC_Condition cond(GT_LE, LC_Expr(ident), - LC_Expr(LC_Ident(LC_Array(LC_Array::MdArray, mdArrInfo->GetArrIndexForDim( - getAllocator(CMK_LoopClone)), - mdArrInfo->dim, LC_Array::None)))); + LC_Array arrLen(LC_Array(LC_Array::MdArray, + mdArrInfo->GetArrIndexForDim(getAllocator(CMK_LoopClone)), mdArrInfo->dim, + LC_Array::None)); + LC_Ident arrLenIdent = LC_Ident(arrLen); + LC_Condition cond(opLimitCondition, LC_Expr(ident), LC_Expr(arrLenIdent)); context->EnsureConditions(loopNum)->Push(cond); + + // TODO: ensure array is dereference-able? } break; @@ -1148,11 +1163,14 @@ bool Compiler::optDeriveLoopCloningConditions(unsigned loopNum, LoopCloneContext return false; } } + JITDUMP("Conditions: "); DBEXEC(verbose, context->PrintConditions(loopNum)); JITDUMP("\n"); + return true; } + return false; } @@ -1398,6 +1416,7 @@ void Compiler::optDebugLogLoopCloning(BasicBlock* block, Statement* insertBefore void Compiler::optPerformStaticOptimizations(unsigned loopNum, LoopCloneContext* context DEBUGARG(bool dynamicPath)) { JitExpandArrayStack* optInfos = context->GetLoopOptInfo(loopNum); + assert(optInfos != nullptr); for (unsigned i = 0; i < optInfos->Size(); ++i) { LcOptInfo* optInfo = optInfos->Get(i); @@ -2547,7 +2566,7 @@ PhaseStatus Compiler::optCloneLoops() { context.CancelLoopOptInfo(i); } - if (allTrue) + else if (allTrue) { // Perform static optimizations on the fast path since we always // have to take the cloned path. From 3649506d45ad221a067d2945b212625b321de78f Mon Sep 17 00:00:00 2001 From: Yusuke Ito Date: Sat, 9 Oct 2021 01:38:38 +0900 Subject: [PATCH 013/106] fix output log message as literal in MetricsEventSource (#60176) --- .../src/System/Diagnostics/Metrics/MetricsEventSource.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MetricsEventSource.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MetricsEventSource.cs index 6d0517bc195e9..9923589b24627 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MetricsEventSource.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MetricsEventSource.cs @@ -374,11 +374,11 @@ private void ParseSpecs(string? metricsSpecs) { if (!MetricSpec.TryParse(specString, out MetricSpec spec)) { - Log.Message("Failed to parse metric spec: {specString}"); + Log.Message($"Failed to parse metric spec: {specString}"); } else { - Log.Message("Parsed metric: {spec}"); + Log.Message($"Parsed metric: {spec}"); if (spec.InstrumentName != null) { _aggregationManager!.Include(spec.MeterName, spec.InstrumentName); From 22b516ca0a4b9851cf993ecc83b2a8238898c465 Mon Sep 17 00:00:00 2001 From: SRV Date: Fri, 8 Oct 2021 20:26:08 +0300 Subject: [PATCH 014/106] Spannified internals of BigInteger (#35565) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Unsafe pointers replaced with managed pointers * Duplicate code replaced with static local * Spannified internals * Spannified compare * Spannified div/rem operations * Minus two array allocations for each bitwise operation * Removed redundant locals * Code review feedback (code style) * Code review feedback * LeadingZeroes replaced with BitOperations * stackalloc in Divide without remainder * Reduced memory allocation for bitwise operations * Managed pointer replaced with Span * Removed namespace imports * Removed delegate type * Reduced memory allocation in the end of each bitwise operation * Code review feedback * Removed redundant array allocation * Trivial division moved to stack alloc * Reduced memory allocation for divide operator * Reduced memory allocation for shift operations * Array pooling for Square operation * Fixed bound check for bitwise operations * Multiply now uses array pooling * Removed namespace import * Reduced memory allocation for Add * Removed swap of arguments * Reduced memory allocations for multiply operator * Reduced memory allocations for modulo operator * Temporarily spannify ActualLength method * Spannify FastReducer * Spannify initial parts of GCD alg * Simple GCD test for debugging * Simple test for debugging * Removed debug code * Spannified GCD algorithm * Removed arg passing by ref * Complete migration of gcd to span * Removed memory allocation for trivial case of GCD * Reorganize utility code * Removed redundant parameter * Reduced memory alloc for trivial case of Pow alg * Use span for trivial modulus pow case * Use span for trivial cases of pow modulus * Fixed buffer cleanup * Reduced memory allocation of ModPow operations * BitsBuffer replaced with span * Reorganize utility methods * Reduced visibility scope * Removed temporary code for bits cleanup * Fill leading bits with zeroes * Unify order of parameters * Unify order of parameters * Unify order of parameters * Reuse the same local var * Removed whtespace * Code review feedback * Avoid misleading with Span.CopyTo * Clarify purpose of slicing Co-authored-by: Günther Foidl * Replace mentioning of uint[] in comments * Fixed code formatting issues * Share StackAllocThreshold const * Ignore const threshold values in Release config * Literal field cannot be discovered with TypeInfo.GetDeclaredField * Removed invalid assertion * Removed invalid assertion * Removed invalid assertion * Code review feedback * Fixed header * Fixed overflow of result buffer * Reduced ctor visibility * Eliminated bounds check * Returned optimization using Math.DivRem * Added braces to be consistent with other branches * Fixed location of the comment * Combine slice and conversion as the single line * Combine slice and conversion as the single line * Merge with parsing optimizations * Reduced checks * Removed redundant global const * Replaced magic consts with their named equivalents * Removed useless asserts * Manual merge with #53984 PR * Review feedback (use const for stackalloc) * Review feedback * Unsafe code replaced with span indexer where possible * Removed unused local * Review feedback Co-authored-by: Günther Foidl --- .../src/System.Runtime.Numerics.csproj | 3 +- .../src/System/Numerics/BigInteger.cs | 871 ++++++++++++------ .../Numerics/BigIntegerCalculator.AddSub.cs | 226 ++--- .../BigIntegerCalculator.BitsBuffer.cs | 215 ----- .../Numerics/BigIntegerCalculator.DivRem.cs | 202 ++-- .../BigIntegerCalculator.FastReducer.cs | 134 ++- .../Numerics/BigIntegerCalculator.GcdInv.cs | 167 ++-- .../Numerics/BigIntegerCalculator.PowMod.cs | 455 +++++---- .../Numerics/BigIntegerCalculator.SquMul.cs | 376 +++----- .../Numerics/BigIntegerCalculator.Utils.cs | 61 ++ .../src/System/Numerics/BigNumber.cs | 12 +- .../src/System/Numerics/Complex.cs | 3 +- .../src/System/Numerics/NumericsHelpers.cs | 53 +- .../tests/BigInteger/BigIntTools.cs | 3 + .../tests/BigInteger/multiply.cs | 2 +- 15 files changed, 1374 insertions(+), 1409 deletions(-) delete mode 100644 src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs create mode 100644 src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs diff --git a/src/libraries/System.Runtime.Numerics/src/System.Runtime.Numerics.csproj b/src/libraries/System.Runtime.Numerics/src/System.Runtime.Numerics.csproj index 7e938de2925ef..d61ea2546b168 100644 --- a/src/libraries/System.Runtime.Numerics/src/System.Runtime.Numerics.csproj +++ b/src/libraries/System.Runtime.Numerics/src/System.Runtime.Numerics.csproj @@ -7,12 +7,12 @@ - + @@ -30,5 +30,6 @@ + diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index d27244837a089..671f4af2699be 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Buffers; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; @@ -15,9 +16,6 @@ namespace System.Numerics private const int kcbitUint = 32; private const int kcbitUlong = 64; private const int DecimalScaleFactorMask = 0x00FF0000; - private const int DecimalSignMask = unchecked((int)0x80000000); - - internal const int StackallocUInt32Limit = 64; // For values int.MinValue < n <= int.MaxValue, the value is stored in sign // and _bits is null. For all other values, sign is +1 or -1 and the bits are in _bits @@ -213,6 +211,7 @@ public BigInteger(decimal value) Debug.Assert(bits.Length == 4 && (bits[3] & DecimalScaleFactorMask) == 0); + const int signMask = unchecked((int)kuMaskHighBit); int size = 3; while (size > 0 && bits[size - 1] == 0) size--; @@ -225,7 +224,7 @@ public BigInteger(decimal value) // bits[0] is the absolute value of this decimal // if bits[0] < 0 then it is too large to be packed into _sign _sign = bits[0]; - _sign *= ((bits[3] & DecimalSignMask) != 0) ? -1 : +1; + _sign *= ((bits[3] & signMask) != 0) ? -1 : +1; _bits = null; } else @@ -241,7 +240,7 @@ public BigInteger(decimal value) _bits[2] = (uint)bits[2]; } - _sign = ((bits[3] & DecimalSignMask) != 0) ? -1 : +1; + _sign = ((bits[3] & signMask) != 0) ? -1 : +1; } AssertValid(); } @@ -473,67 +472,51 @@ internal BigInteger(int n, uint[]? rgu) /// /// Constructor used during bit manipulation and arithmetic. - /// When possible the span will be packed into _sign to conserve space. + /// When possible the value will be packed into _sign to conserve space. /// /// The absolute value of the number - /// An array, identical in contents to , for which ownership can be transferred; null if no such array was available. /// The bool indicating the sign of the value. - private BigInteger(ReadOnlySpan value, uint[]? valueArray, bool negative) + private BigInteger(ReadOnlySpan value, bool negative) { - Debug.Assert(valueArray is null || value.SequenceEqual(valueArray)); + int len; - // Try to conserve space as much as possible by checking for wasted leading uint entries - // (sometimes the span has leading zeros from bit manipulation operations & and ^). - if (!value.IsEmpty && value[^1] == 0) - { - int len = value.Length - 1; - while (len > 0 && value[len - 1] == 0) - { - len--; - } - value = value.Slice(0, len); - valueArray = null; - } + // Try to conserve space as much as possible by checking for wasted leading span entries + // sometimes the span has leading zeros from bit manipulation operations & and ^ + for (len = value.Length; len > 0 && value[len - 1] == 0; len--); - if (value.IsEmpty) + if (len == 0) { this = s_bnZeroInt; } - else if (value.Length == 1 && value[0] < kuMaskHighBit) + else if (len == 1 && value[0] < kuMaskHighBit) { - // Values like (Int32.MaxValue+1) are stored as "0x80000000" and as such cannot be packed into _sign. + // Values like (Int32.MaxValue+1) are stored as "0x80000000" and as such cannot be packed into _sign _sign = negative ? -(int)value[0] : (int)value[0]; _bits = null; if (_sign == int.MinValue) { - // Although Int32.MinValue fits in _sign, we represent this case differently for negate. + // Although Int32.MinValue fits in _sign, we represent this case differently for negate this = s_bnMinInt; } } else { _sign = negative ? -1 : +1; - _bits = valueArray ?? value.ToArray(); + _bits = value.Slice(0, len).ToArray(); } - AssertValid(); } /// - /// Create a BigInteger from a little-endian twos-complement UInt32 array. - /// When possible, value is assigned directly to this._bits without an array copy - /// so use this ctor with care. + /// Create a BigInteger from a little-endian twos-complement UInt32 span. /// /// - private BigInteger(uint[] value) + private BigInteger(Span value) { - if (value == null) - throw new ArgumentNullException(nameof(value)); - int dwordCount = value.Length; - bool isNegative = dwordCount > 0 && ((value[dwordCount - 1] & 0x80000000) == 0x80000000); + bool isNegative = dwordCount > 0 && ((value[dwordCount - 1] & kuMaskHighBit) == kuMaskHighBit); - // Try to conserve space as much as possible by checking for wasted leading uint[] entries + // Try to conserve space as much as possible by checking for wasted leading span entries while (dwordCount > 0 && value[dwordCount - 1] == 0) dwordCount--; if (dwordCount == 0) @@ -568,18 +551,9 @@ private BigInteger(uint[] value) if (!isNegative) { // Handle the simple positive value cases where the input is already in sign magnitude - if (dwordCount != value.Length) - { - _sign = +1; - _bits = new uint[dwordCount]; - Array.Copy(value, _bits, dwordCount); - } - // No trimming is possible. Assign value directly to _bits. - else - { - _sign = +1; - _bits = value; - } + _sign = +1; + value = value.Slice(0, dwordCount); + _bits = value.ToArray(); AssertValid(); return; } @@ -608,29 +582,20 @@ private BigInteger(uint[] value) _bits = null; } } - // The number is represented by multiple dwords. - // Trim off any wasted uint values when possible. - else if (len != value.Length) - { - _sign = -1; - _bits = new uint[len]; - Array.Copy(value, _bits, len); - } - // No trimming is possible. Assign value directly to _bits. else { _sign = -1; - _bits = value; + _bits = value.Slice(0, len).ToArray(); } AssertValid(); return; } - public static BigInteger Zero => s_bnZeroInt; + public static BigInteger Zero { get { return s_bnZeroInt; } } - public static BigInteger One => s_bnOneInt; + public static BigInteger One { get { return s_bnOneInt; } } - public static BigInteger MinusOne => s_bnMinusOneInt; + public static BigInteger MinusOne { get { return s_bnMinusOneInt; } } public bool IsPowerOfTwo { @@ -639,12 +604,12 @@ public bool IsPowerOfTwo AssertValid(); if (_bits == null) - return (_sign & (_sign - 1)) == 0 && _sign != 0; + return BitOperations.IsPow2(_sign); if (_sign != 1) return false; int iu = _bits.Length - 1; - if ((_bits[iu] & (_bits[iu] - 1)) != 0) + if (!BitOperations.IsPow2(_bits[iu])) return false; while (--iu >= 0) { @@ -774,10 +739,26 @@ public static BigInteger DivRem(BigInteger dividend, BigInteger divisor, out Big if (trivialDivisor) { uint rest; - uint[] bits = BigIntegerCalculator.Divide(dividend._bits, NumericsHelpers.Abs(divisor._sign), out rest); - remainder = dividend._sign < 0 ? -1 * rest : rest; - return new BigInteger(bits, bits, (dividend._sign < 0) ^ (divisor._sign < 0)); + uint[]? bitsFromPool = null; + int size = dividend._bits.Length; + Span quotient = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); + + try + { + // may throw DivideByZeroException + BigIntegerCalculator.Divide(dividend._bits, NumericsHelpers.Abs(divisor._sign), quotient, out rest); + + remainder = dividend._sign < 0 ? -1 * rest : rest; + return new BigInteger(quotient, (dividend._sign < 0) ^ (divisor._sign < 0)); + } + finally + { + if (bitsFromPool != null) + ArrayPool.Shared.Return(bitsFromPool); + } } Debug.Assert(divisor._bits != null); @@ -789,11 +770,30 @@ public static BigInteger DivRem(BigInteger dividend, BigInteger divisor, out Big } else { - uint[] rest; - uint[] bits = BigIntegerCalculator.Divide(dividend._bits, divisor._bits, out rest); + uint[]? remainderFromPool = null; + int size = dividend._bits.Length; + Span rest = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : remainderFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); - remainder = new BigInteger(rest, rest, dividend._sign < 0); - return new BigInteger(bits, bits, (dividend._sign < 0) ^ (divisor._sign < 0)); + uint[]? quotientFromPool = null; + size = dividend._bits.Length - divisor._bits.Length + 1; + Span quotient = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : quotientFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); + + BigIntegerCalculator.Divide(dividend._bits, divisor._bits, quotient, rest); + + remainder = new BigInteger(rest, dividend._sign < 0); + var result = new BigInteger(quotient, (dividend._sign < 0) ^ (divisor._sign < 0)); + + if (remainderFromPool != null) + ArrayPool.Shared.Return(remainderFromPool); + + if (quotientFromPool != null) + ArrayPool.Shared.Return(quotientFromPool); + + return result; } } @@ -823,7 +823,7 @@ public static double Log(BigInteger value, double baseValue) ulong l = value._bits.Length > 2 ? value._bits[value._bits.Length - 3] : 0; // Measure the exact bit count - int c = NumericsHelpers.CbitHighZero((uint)h); + int c = BitOperations.LeadingZeroCount((uint)h); long b = (long)value._bits.Length * 32 - c; // Extract most significant bits @@ -857,7 +857,7 @@ public static BigInteger GreatestCommonDivisor(BigInteger left, BigInteger right Debug.Assert(right._bits != null); return left._sign != 0 ? BigIntegerCalculator.Gcd(right._bits, NumericsHelpers.Abs(left._sign)) - : new BigInteger(right._bits, null, negative: false); + : new BigInteger(right._bits, negative: false); } if (trivialRight) @@ -865,7 +865,7 @@ public static BigInteger GreatestCommonDivisor(BigInteger left, BigInteger right Debug.Assert(left._bits != null); return right._sign != 0 ? BigIntegerCalculator.Gcd(left._bits, NumericsHelpers.Abs(right._sign)) - : new BigInteger(left._bits, null, negative: false); + : new BigInteger(left._bits, negative: false); } Debug.Assert(left._bits != null && right._bits != null); @@ -880,29 +880,46 @@ public static BigInteger GreatestCommonDivisor(BigInteger left, BigInteger right } } - private static BigInteger GreatestCommonDivisor(uint[] leftBits, uint[] rightBits) + private static BigInteger GreatestCommonDivisor(ReadOnlySpan leftBits, ReadOnlySpan rightBits) { Debug.Assert(BigIntegerCalculator.Compare(leftBits, rightBits) >= 0); + uint[]? bitsFromPool = null; + BigInteger result; + // Short circuits to spare some allocations... if (rightBits.Length == 1) { uint temp = BigIntegerCalculator.Remainder(leftBits, rightBits[0]); - return BigIntegerCalculator.Gcd(rightBits[0], temp); + result = BigIntegerCalculator.Gcd(rightBits[0], temp); } - - if (rightBits.Length == 2) + else if (rightBits.Length == 2) { - uint[] tempBits = BigIntegerCalculator.Remainder(leftBits, rightBits); + Span bits = (leftBits.Length <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(leftBits.Length)).Slice(0, leftBits.Length); + + BigIntegerCalculator.Remainder(leftBits, rightBits, bits); ulong left = ((ulong)rightBits[1] << 32) | rightBits[0]; - ulong right = ((ulong)tempBits[1] << 32) | tempBits[0]; + ulong right = ((ulong)bits[1] << 32) | bits[0]; + + result = BigIntegerCalculator.Gcd(left, right); + } + else + { + Span bits = (leftBits.Length <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(leftBits.Length)).Slice(0, leftBits.Length); - return BigIntegerCalculator.Gcd(left, right); + BigIntegerCalculator.Gcd(leftBits, rightBits, bits); + result = new BigInteger(bits, negative: false); } - uint[] bits = BigIntegerCalculator.Gcd(leftBits, rightBits); - return new BigInteger(bits, bits, negative: false); + if (bitsFromPool != null) + ArrayPool.Shared.Return(bitsFromPool); + + return result; } public static BigInteger Max(BigInteger left, BigInteger right) @@ -932,6 +949,8 @@ public static BigInteger ModPow(BigInteger value, BigInteger exponent, BigIntege bool trivialExponent = exponent._bits == null; bool trivialModulus = modulus._bits == null; + BigInteger result; + if (trivialModulus) { uint bits = trivialValue && trivialExponent ? BigIntegerCalculator.Pow(NumericsHelpers.Abs(value._sign), NumericsHelpers.Abs(exponent._sign), NumericsHelpers.Abs(modulus._sign)) : @@ -939,17 +958,43 @@ public static BigInteger ModPow(BigInteger value, BigInteger exponent, BigIntege trivialExponent ? BigIntegerCalculator.Pow(value._bits!, NumericsHelpers.Abs(exponent._sign), NumericsHelpers.Abs(modulus._sign)) : BigIntegerCalculator.Pow(value._bits!, exponent._bits!, NumericsHelpers.Abs(modulus._sign)); - return value._sign < 0 && !exponent.IsEven ? -1 * bits : bits; + result = value._sign < 0 && !exponent.IsEven ? -1 * bits : bits; } else { - uint[] bits = trivialValue && trivialExponent ? BigIntegerCalculator.Pow(NumericsHelpers.Abs(value._sign), NumericsHelpers.Abs(exponent._sign), modulus._bits!) : - trivialValue ? BigIntegerCalculator.Pow(NumericsHelpers.Abs(value._sign), exponent._bits!, modulus._bits!) : - trivialExponent ? BigIntegerCalculator.Pow(value._bits!, NumericsHelpers.Abs(exponent._sign), modulus._bits!) : - BigIntegerCalculator.Pow(value._bits!, exponent._bits!, modulus._bits!); + int size = (modulus._bits?.Length ?? 1) << 1; + uint[]? bitsFromPool = null; + Span bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); + bits.Clear(); + if (trivialValue) + { + if (trivialExponent) + { + BigIntegerCalculator.Pow(NumericsHelpers.Abs(value._sign), NumericsHelpers.Abs(exponent._sign), modulus._bits!, bits); + } + else + { + BigIntegerCalculator.Pow(NumericsHelpers.Abs(value._sign), exponent._bits!, modulus._bits!, bits); + } + } + else if (trivialExponent) + { + BigIntegerCalculator.Pow(value._bits!, NumericsHelpers.Abs(exponent._sign), modulus._bits!, bits); + } + else + { + BigIntegerCalculator.Pow(value._bits!, exponent._bits!, modulus._bits!, bits); + } + + result = new BigInteger(bits, value._sign < 0 && !exponent.IsEven); - return new BigInteger(bits, bits, value._sign < 0 && !exponent.IsEven); + if (bitsFromPool != null) + ArrayPool.Shared.Return(bitsFromPool); } + + return result; } public static BigInteger Pow(BigInteger value, int exponent) @@ -966,6 +1011,10 @@ public static BigInteger Pow(BigInteger value, int exponent) bool trivialValue = value._bits == null; + uint power = NumericsHelpers.Abs(exponent); + uint[]? bitsFromPool = null; + BigInteger result; + if (trivialValue) { if (value._sign == 1) @@ -974,34 +1023,54 @@ public static BigInteger Pow(BigInteger value, int exponent) return (exponent & 1) != 0 ? value : s_bnOneInt; if (value._sign == 0) return value; + + int size = BigIntegerCalculator.PowBound(power, 1); + Span bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); + bits.Clear(); + + BigIntegerCalculator.Pow(NumericsHelpers.Abs(value._sign), power, bits); + result = new BigInteger(bits, value._sign < 0 && (exponent & 1) != 0); + } + else + { + int size = BigIntegerCalculator.PowBound(power, value._bits!.Length); + Span bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); + bits.Clear(); + + BigIntegerCalculator.Pow(value._bits, power, bits); + result = new BigInteger(bits, value._sign < 0 && (exponent & 1) != 0); } - uint[] bits = trivialValue - ? BigIntegerCalculator.Pow(NumericsHelpers.Abs(value._sign), NumericsHelpers.Abs(exponent)) - : BigIntegerCalculator.Pow(value._bits!, NumericsHelpers.Abs(exponent)); + if (bitsFromPool != null) + ArrayPool.Shared.Return(bitsFromPool); - return new BigInteger(bits, bits, value._sign < 0 && (exponent & 1) != 0); + return result; } public override int GetHashCode() { AssertValid(); - if (_bits == null) + if (_bits is null) return _sign; + int hash = _sign; for (int iv = _bits.Length; --iv >= 0;) - hash = NumericsHelpers.CombineHash(hash, unchecked((int)_bits[iv])); + hash = unchecked((int)CombineHash((uint)hash, _bits[iv])); return hash; + + static uint CombineHash(uint u1, uint u2) => ((u1 << 7) | (u1 >> 25)) ^ u2; } public override bool Equals([NotNullWhen(true)] object? obj) { AssertValid(); - if (!(obj is BigInteger)) - return false; - return Equals((BigInteger)obj); + return obj is BigInteger other && Equals(other); } public bool Equals(long other) @@ -1124,9 +1193,9 @@ public int CompareTo(object? obj) { if (obj == null) return 1; - if (!(obj is BigInteger)) + if (obj is not BigInteger bigInt) throw new ArgumentException(SR.Argument_MustBeBigInt, nameof(obj)); - return CompareTo((BigInteger)obj); + return CompareTo(bigInt); } /// @@ -1424,79 +1493,59 @@ private enum GetBytesMode { AllocateArray, Count, Span } } /// - /// Return the value of this BigInteger as a little-endian twos-complement - /// uint span, using the fewest number of uints possible. If the value is zero, - /// return a span of one uint whose element is 0. + /// Converts the value of this BigInteger to a little-endian twos-complement + /// uint span allocated by the caller using the fewest number of uints possible. /// - private ReadOnlySpan ToUInt32Span(Span scratch) + /// Pre-allocated buffer by the caller. + /// The actual number of copied elements. + private int WriteTo(Span buffer) { - Debug.Assert(!scratch.IsEmpty); - - if (_bits == null && _sign == 0) - { - scratch[0] = 0; - return scratch.Slice(0, 1); - } + Debug.Assert(_bits is null || _sign == 0 ? buffer.Length == 2 : buffer.Length >= _bits.Length + 1); - Span dwords = scratch; - bool dwordsIsScratch = true; uint highDWord; - if (_bits == null) + if (_bits is null) { - dwords[0] = unchecked((uint)_sign); - dwords = dwords.Slice(0, 1); + buffer[0] = unchecked((uint)_sign); highDWord = (_sign < 0) ? uint.MaxValue : 0; } - else if (_sign == -1) + else { - if (dwords.Length >= _bits.Length) + _bits.CopyTo(buffer); + buffer = buffer.Slice(0, _bits.Length + 1); + if (_sign == -1) { - _bits.AsSpan().CopyTo(dwords); - dwords = dwords.Slice(0, _bits.Length); + NumericsHelpers.DangerousMakeTwosComplement(buffer.Slice(0, buffer.Length - 1)); // Mutates dwords + highDWord = uint.MaxValue; } else - { - dwords = (uint[])_bits.Clone(); - } - NumericsHelpers.DangerousMakeTwosComplement(dwords); // Mutates dwords - highDWord = uint.MaxValue; - } - else - { - dwords = _bits; - highDWord = 0; - dwordsIsScratch = false; + highDWord = 0; } // Find highest significant byte and ensure high bit is 0 if positive, 1 if negative - int msb; - for (msb = dwords.Length - 1; msb > 0 && dwords[msb] == highDWord; msb--); - bool needExtraByte = (dwords[msb] & 0x80000000) != (highDWord & 0x80000000); - - int length = msb + 1 + (needExtraByte ? 1 : 0); - bool copyDwordsToScratch = true; - if (length <= scratch.Length) - { - scratch = scratch.Slice(0, length); - copyDwordsToScratch = !dwordsIsScratch; - } - else + int msb = buffer.Length - 2; + while (msb > 0 && buffer[msb] == highDWord) { - scratch = new uint[length]; + msb--; } - if (copyDwordsToScratch) - { - dwords.Slice(0, msb + 1).CopyTo(scratch); - } + // Ensure high bit is 0 if positive, 1 if negative + bool needExtraByte = (buffer[msb] & 0x80000000) != (highDWord & 0x80000000); + int count; if (needExtraByte) { - scratch[^1] = highDWord; + count = msb + 2; + buffer = buffer.Slice(0, count); + buffer[buffer.Length - 1] = highDWord; + } + else + { + count = msb + 1; + buffer = buffer.Slice(0, count); } - return scratch; + return count; } public override string ToString() @@ -1524,42 +1573,72 @@ public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan return BigNumber.TryFormatBigInteger(this, format, NumberFormatInfo.GetInstance(provider), destination, out charsWritten); } - private static BigInteger Add(uint[]? leftBits, int leftSign, uint[]? rightBits, int rightSign) + private static BigInteger Add(ReadOnlySpan leftBits, int leftSign, ReadOnlySpan rightBits, int rightSign) { - bool trivialLeft = leftBits == null; - bool trivialRight = rightBits == null; + bool trivialLeft = leftBits.IsEmpty; + bool trivialRight = rightBits.IsEmpty; if (trivialLeft && trivialRight) { return (long)leftSign + rightSign; } + BigInteger result; + uint[]? bitsFromPool = null; + if (trivialLeft) { - Debug.Assert(rightBits != null); - uint[] bits = BigIntegerCalculator.Add(rightBits, NumericsHelpers.Abs(leftSign)); - return new BigInteger(bits, bits, leftSign < 0); - } + Debug.Assert(!rightBits.IsEmpty); - if (trivialRight) - { - Debug.Assert(leftBits != null); - uint[] bits = BigIntegerCalculator.Add(leftBits, NumericsHelpers.Abs(rightSign)); - return new BigInteger(bits, bits, leftSign < 0); + int size = rightBits.Length + 1; + Span bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); + + BigIntegerCalculator.Add(rightBits, NumericsHelpers.Abs(leftSign), bits); + result = new BigInteger(bits, leftSign < 0); } + else if (trivialRight) + { + Debug.Assert(!leftBits.IsEmpty); - Debug.Assert(leftBits != null && rightBits != null); + int size = leftBits.Length + 1; + Span bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); - if (leftBits.Length < rightBits.Length) + BigIntegerCalculator.Add(leftBits, NumericsHelpers.Abs(rightSign), bits); + result = new BigInteger(bits, leftSign < 0); + } + else if (leftBits.Length < rightBits.Length) { - uint[] bits = BigIntegerCalculator.Add(rightBits, leftBits); - return new BigInteger(bits, bits, leftSign < 0); + Debug.Assert(!leftBits.IsEmpty && !rightBits.IsEmpty); + + int size = rightBits.Length + 1; + Span bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); + + BigIntegerCalculator.Add(rightBits, leftBits, bits); + result = new BigInteger(bits, leftSign < 0); } else { - uint[] bits = BigIntegerCalculator.Add(leftBits, rightBits); - return new BigInteger(bits, bits, leftSign < 0); + Debug.Assert(!leftBits.IsEmpty && !rightBits.IsEmpty); + + int size = leftBits.Length + 1; + Span bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); + + BigIntegerCalculator.Add(leftBits, rightBits, bits); + result = new BigInteger(bits, leftSign < 0); } + + if (bitsFromPool != null) + ArrayPool.Shared.Return(bitsFromPool); + + return result; } public static BigInteger operator -(BigInteger left, BigInteger right) @@ -1572,42 +1651,70 @@ private static BigInteger Add(uint[]? leftBits, int leftSign, uint[]? rightBits, return Subtract(left._bits, left._sign, right._bits, right._sign); } - private static BigInteger Subtract(uint[]? leftBits, int leftSign, uint[]? rightBits, int rightSign) + private static BigInteger Subtract(ReadOnlySpan leftBits, int leftSign, ReadOnlySpan rightBits, int rightSign) { - bool trivialLeft = leftBits == null; - bool trivialRight = rightBits == null; + bool trivialLeft = leftBits.IsEmpty; + bool trivialRight = rightBits.IsEmpty; if (trivialLeft && trivialRight) { return (long)leftSign - rightSign; } + BigInteger result; + uint[]? bitsFromPool = null; + if (trivialLeft) { - Debug.Assert(rightBits != null); - uint[] bits = BigIntegerCalculator.Subtract(rightBits, NumericsHelpers.Abs(leftSign)); - return new BigInteger(bits, bits, leftSign >= 0); - } + Debug.Assert(!rightBits.IsEmpty); - if (trivialRight) - { - Debug.Assert(leftBits != null); - uint[] bits = BigIntegerCalculator.Subtract(leftBits, NumericsHelpers.Abs(rightSign)); - return new BigInteger(bits, bits, leftSign < 0); + int size = rightBits.Length; + Span bits = (size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); + + BigIntegerCalculator.Subtract(rightBits, NumericsHelpers.Abs(leftSign), bits); + result = new BigInteger(bits, leftSign >= 0); } + else if (trivialRight) + { + Debug.Assert(!leftBits.IsEmpty); - Debug.Assert(leftBits != null && rightBits != null); + int size = leftBits.Length; + Span bits = (size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); - if (BigIntegerCalculator.Compare(leftBits, rightBits) < 0) + BigIntegerCalculator.Subtract(leftBits, NumericsHelpers.Abs(rightSign), bits); + result = new BigInteger(bits, leftSign < 0); + } + else if (BigIntegerCalculator.Compare(leftBits, rightBits) < 0) { - uint[] bits = BigIntegerCalculator.Subtract(rightBits, leftBits); - return new BigInteger(bits, bits, leftSign >= 0); + int size = rightBits.Length; + Span bits = (size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); + + BigIntegerCalculator.Subtract(rightBits, leftBits, bits); + result = new BigInteger(bits, leftSign >= 0); } else { - uint[] bits = BigIntegerCalculator.Subtract(leftBits, rightBits); - return new BigInteger(bits, bits, leftSign < 0); + Debug.Assert(!leftBits.IsEmpty && !rightBits.IsEmpty); + + int size = leftBits.Length; + Span bits = (size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); + + BigIntegerCalculator.Subtract(leftBits, rightBits, bits); + result = new BigInteger(bits, leftSign < 0); } + + if (bitsFromPool != null) + ArrayPool.Shared.Return(bitsFromPool); + + return result; } public static implicit operator BigInteger(byte value) @@ -1822,7 +1929,7 @@ public static explicit operator double(BigInteger value) ulong m = length > 1 ? bits[length - 2] : 0; ulong l = length > 2 ? bits[length - 3] : 0; - int z = NumericsHelpers.CbitHighZero((uint)h); + int z = BitOperations.LeadingZeroCount((uint)h); int exp = (length - 2) * 32 - z; ulong man = (h << 32 + z) | (m << z) | (l >> 32 - z); @@ -1858,24 +1965,53 @@ public static explicit operator decimal(BigInteger value) return Zero; } - if (left._bits == null && right._bits == null) + if (left._bits is null && right._bits is null) { return left._sign & right._sign; } - ReadOnlySpan x = left.ToUInt32Span(stackalloc uint[StackallocUInt32Limit / 2]); - ReadOnlySpan y = right.ToUInt32Span(stackalloc uint[StackallocUInt32Limit / 2]); - uint[] z = new uint[Math.Max(x.Length, y.Length)]; uint xExtend = (left._sign < 0) ? uint.MaxValue : 0; uint yExtend = (right._sign < 0) ? uint.MaxValue : 0; + uint[]? leftBufferFromPool = null; + int size = (left._bits?.Length ?? 1) + 1; + Span x = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : leftBufferFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); + x = x.Slice(0, left.WriteTo(x)); + + uint[]? rightBufferFromPool = null; + size = (right._bits?.Length ?? 1) + 1; + Span y = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : rightBufferFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); + y = y.Slice(0, right.WriteTo(y)); + + uint[]? resultBufferFromPool = null; + size = Math.Max(x.Length, y.Length); + Span z = (size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : resultBufferFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); + for (int i = 0; i < z.Length; i++) { - uint xu = (i < x.Length) ? x[i] : xExtend; - uint yu = (i < y.Length) ? y[i] : yExtend; + uint xu = ((uint)i < (uint)x.Length) ? x[i] : xExtend; + uint yu = ((uint)i < (uint)y.Length) ? y[i] : yExtend; z[i] = xu & yu; } - return new BigInteger(z); + + if (leftBufferFromPool != null) + ArrayPool.Shared.Return(leftBufferFromPool); + + if (rightBufferFromPool != null) + ArrayPool.Shared.Return(rightBufferFromPool); + + var result = new BigInteger(z); + + if (resultBufferFromPool != null) + ArrayPool.Shared.Return(resultBufferFromPool); + + return result; } public static BigInteger operator |(BigInteger left, BigInteger right) @@ -1885,47 +2021,104 @@ public static explicit operator decimal(BigInteger value) if (right.IsZero) return left; - if (left._bits == null && right._bits == null) + if (left._bits is null && right._bits is null) { return left._sign | right._sign; } - ReadOnlySpan x = left.ToUInt32Span(stackalloc uint[StackallocUInt32Limit / 2]); - ReadOnlySpan y = right.ToUInt32Span(stackalloc uint[StackallocUInt32Limit / 2]); - uint[] z = new uint[Math.Max(x.Length, y.Length)]; uint xExtend = (left._sign < 0) ? uint.MaxValue : 0; uint yExtend = (right._sign < 0) ? uint.MaxValue : 0; + uint[]? leftBufferFromPool = null; + int size = (left._bits?.Length ?? 1) + 1; + Span x = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : leftBufferFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); + x = x.Slice(0, left.WriteTo(x)); + + uint[]? rightBufferFromPool = null; + size = (right._bits?.Length ?? 1) + 1; + Span y = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : rightBufferFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); + y = y.Slice(0, right.WriteTo(y)); + + uint[]? resultBufferFromPool = null; + size = Math.Max(x.Length, y.Length); + Span z = (size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : resultBufferFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); + for (int i = 0; i < z.Length; i++) { - uint xu = (i < x.Length) ? x[i] : xExtend; - uint yu = (i < y.Length) ? y[i] : yExtend; + uint xu = ((uint)i < (uint)x.Length) ? x[i] : xExtend; + uint yu = ((uint)i < (uint)y.Length) ? y[i] : yExtend; z[i] = xu | yu; } - return new BigInteger(z); + + if (leftBufferFromPool != null) + ArrayPool.Shared.Return(leftBufferFromPool); + + if (rightBufferFromPool != null) + ArrayPool.Shared.Return(rightBufferFromPool); + + var result = new BigInteger(z); + + if (resultBufferFromPool != null) + ArrayPool.Shared.Return(resultBufferFromPool); + + return result; } public static BigInteger operator ^(BigInteger left, BigInteger right) { - if (left._bits == null && right._bits == null) + if (left._bits is null && right._bits is null) { return left._sign ^ right._sign; } - ReadOnlySpan x = left.ToUInt32Span(stackalloc uint[StackallocUInt32Limit / 2]); - ReadOnlySpan y = right.ToUInt32Span(stackalloc uint[StackallocUInt32Limit / 2]); - uint[] z = new uint[Math.Max(x.Length, y.Length)]; uint xExtend = (left._sign < 0) ? uint.MaxValue : 0; uint yExtend = (right._sign < 0) ? uint.MaxValue : 0; + uint[]? leftBufferFromPool = null; + int size = (left._bits?.Length ?? 1) + 1; + Span x = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : leftBufferFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); + x = x.Slice(0, left.WriteTo(x)); + + uint[]? rightBufferFromPool = null; + size = (right._bits?.Length ?? 1) + 1; + Span y = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : rightBufferFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); + y = y.Slice(0, right.WriteTo(y)); + + uint[]? resultBufferFromPool = null; + size = Math.Max(x.Length, y.Length); + Span z = (size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : resultBufferFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); + for (int i = 0; i < z.Length; i++) { - uint xu = (i < x.Length) ? x[i] : xExtend; - uint yu = (i < y.Length) ? y[i] : yExtend; + uint xu = ((uint)i < (uint)x.Length) ? x[i] : xExtend; + uint yu = ((uint)i < (uint)y.Length) ? y[i] : yExtend; z[i] = xu ^ yu; } - return new BigInteger(z); + if (leftBufferFromPool != null) + ArrayPool.Shared.Return(leftBufferFromPool); + + if (rightBufferFromPool != null) + ArrayPool.Shared.Return(rightBufferFromPool); + + var result = new BigInteger(z); + + if (resultBufferFromPool != null) + ArrayPool.Shared.Return(resultBufferFromPool); + + return result; } public static BigInteger operator <<(BigInteger value, int shift) @@ -1934,28 +2127,26 @@ public static explicit operator decimal(BigInteger value) return value; if (shift == int.MinValue) - return (value >> int.MaxValue) >> 1; + return ((value >> int.MaxValue) >> 1); if (shift < 0) return value >> -shift; (int digitShift, int smallShift) = Math.DivRem(shift, kcbitUint); - Span xd = stackalloc uint[1]; - bool negx = GetPartsForBitManipulation(ref value, ref xd); + uint[]? xdFromPool = null; + int xl = value._bits?.Length ?? 1; + Span xd = (xl <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : xdFromPool = ArrayPool.Shared.Rent(xl)).Slice(0, xl); + bool negx = value.GetPartsForBitManipulation(xd); - int zl = xd.Length + digitShift + 1; - uint[]? zdArray = null; - Span zd = stackalloc uint[0]; - if (zl <= StackallocUInt32Limit) - { - zd = stackalloc uint[StackallocUInt32Limit].Slice(0, zl); - zd.Slice(0, digitShift).Clear(); - } - else - { - zd = zdArray = new uint[zl]; - } + int zl = xl + digitShift + 1; + uint[]? zdFromPool = null; + Span zd = ((uint)zl <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : zdFromPool = ArrayPool.Shared.Rent(zl)).Slice(0, zl); + zd.Clear(); uint carry = 0; if (smallShift == 0) @@ -1968,16 +2159,25 @@ public static explicit operator decimal(BigInteger value) else { int carryShift = kcbitUint - smallShift; - for (int i = 0; i < xd.Length; i++) + int i; + for (i = 0; i < xd.Length; i++) { uint rot = xd[i]; zd[i + digitShift] = rot << smallShift | carry; carry = rot >> carryShift; } } - zd[^1] = carry; - return new BigInteger(zd, zdArray, negx); + zd[zd.Length - 1] = carry; + + var result = new BigInteger(zd, negx); + + if (xdFromPool != null) + ArrayPool.Shared.Return(xdFromPool); + if (zdFromPool != null) + ArrayPool.Shared.Return(zdFromPool); + + return result; } public static BigInteger operator >>(BigInteger value, int shift) @@ -1986,38 +2186,30 @@ public static explicit operator decimal(BigInteger value) return value; if (shift == int.MinValue) - return value << int.MaxValue << 1; + return ((value << int.MaxValue) << 1); if (shift < 0) return value << -shift; (int digitShift, int smallShift) = Math.DivRem(shift, kcbitUint); - Span stackallocedXd = stackalloc uint[1]; - Span xd = stackallocedXd; - bool negx = GetPartsForBitManipulation(ref value, ref xd); + BigInteger result; + + uint[]? xdFromPool = null; + int xl = value._bits?.Length ?? 1; + Span xd = (xl <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : xdFromPool = ArrayPool.Shared.Rent(xl)).Slice(0, xl); + + bool negx = value.GetPartsForBitManipulation(xd); bool trackSignBit = false; if (negx) { if (shift >= (kcbitUint * xd.Length)) { - return MinusOne; - } - - if (xd != stackallocedXd) - { - // make a copy of the part extracted from GetPartsForBitManipulation - if (xd.Length <= StackallocUInt32Limit) - { - stackallocedXd = stackalloc uint[StackallocUInt32Limit].Slice(0, xd.Length); - xd.CopyTo(stackallocedXd); - xd = stackallocedXd; - } - else - { - xd = xd.ToArray(); - } + result = MinusOne; + goto exit; } NumericsHelpers.DangerousMakeTwosComplement(xd); // Mutates xd @@ -2028,18 +2220,15 @@ public static explicit operator decimal(BigInteger value) // After a 32 bit right shift, it becomes [0x00, 0x00] which is [0x00, 0x00] when converted back. // The expected result is [0x00, 0x00, 0xFFFFFFFF] (2's complement) or [0x00, 0x00, 0x01] when converted back // If the 2's component's last element is a 0, we will track the sign externally - trackSignBit = smallShift == 0 && xd[^1] == 0; + trackSignBit = smallShift == 0 && xd[xd.Length - 1] == 0; } - int zl = xd.Length - digitShift + (trackSignBit ? 1: 0); - uint[]? zdArray = null; - Span zd = stackalloc uint[0]; - if (zl > 0) - { - zd = zl <= StackallocUInt32Limit ? - stackalloc uint[StackallocUInt32Limit].Slice(0, zl) : - zdArray = new uint[zl]; - } + uint[]? zdFromPool = null; + int zl = Math.Max(xl - digitShift, 0) + (trackSignBit ? 1 : 0); + Span zd = ((uint)zl <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : zdFromPool = ArrayPool.Shared.Rent(zl)).Slice(0, zl); + zd.Clear(); if (smallShift == 0) { @@ -2063,17 +2252,25 @@ stackalloc uint[StackallocUInt32Limit].Slice(0, zl) : carry = rot << carryShift; } } + if (negx) { // Set the tracked sign to the last element if (trackSignBit) - { - zd[^1] = 0xFFFFFFFF; - } + zd[zd.Length - 1] = 0xFFFFFFFF; + NumericsHelpers.DangerousMakeTwosComplement(zd); // Mutates zd } - return new BigInteger(zd, zdArray, negx); + result = new BigInteger(zd, negx); + + if (zdFromPool != null) + ArrayPool.Shared.Return(zdFromPool); + exit: + if (xdFromPool != null) + ArrayPool.Shared.Return(xdFromPool); + + return result; } public static BigInteger operator ~(BigInteger value) @@ -2118,46 +2315,87 @@ stackalloc uint[StackallocUInt32Limit].Slice(0, zl) : left.AssertValid(); right.AssertValid(); - bool trivialLeft = left._bits == null; - bool trivialRight = right._bits == null; + return Multiply(left._bits, left._sign, right._bits, right._sign); + } + + private static BigInteger Multiply(ReadOnlySpan left, int leftSign, ReadOnlySpan right, int rightSign) + { + bool trivialLeft = left.IsEmpty; + bool trivialRight = right.IsEmpty; if (trivialLeft && trivialRight) { - return (long)left._sign * right._sign; + return (long)leftSign * rightSign; } + BigInteger result; + uint[]? bitsFromPool = null; + if (trivialLeft) { - Debug.Assert(right._bits != null); - uint[] bits = BigIntegerCalculator.Multiply(right._bits, NumericsHelpers.Abs(left._sign)); - return new BigInteger(bits, bits, (left._sign < 0) ^ (right._sign < 0)); - } + Debug.Assert(!right.IsEmpty); - if (trivialRight) - { - Debug.Assert(left._bits != null); - uint[] bits = BigIntegerCalculator.Multiply(left._bits, NumericsHelpers.Abs(right._sign)); - return new BigInteger(bits, bits, (left._sign < 0) ^ (right._sign < 0)); + int size = right.Length + 1; + Span bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); + + BigIntegerCalculator.Multiply(right, NumericsHelpers.Abs(leftSign), bits); + result = new BigInteger(bits, (leftSign < 0) ^ (rightSign < 0)); } + else if (trivialRight) + { + Debug.Assert(!left.IsEmpty); - Debug.Assert(left._bits != null && right._bits != null); + int size = left.Length + 1; + Span bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); - if (left._bits == right._bits) - { - uint[] bits = BigIntegerCalculator.Square(left._bits); - return new BigInteger(bits, bits, (left._sign < 0) ^ (right._sign < 0)); + BigIntegerCalculator.Multiply(left, NumericsHelpers.Abs(rightSign), bits); + result = new BigInteger(bits, (leftSign < 0) ^ (rightSign < 0)); } + else if (left == right) + { + int size = left.Length + right.Length; + Span bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); - if (left._bits.Length < right._bits.Length) + BigIntegerCalculator.Square(left, bits); + result = new BigInteger(bits, negative: false); + } + else if (left.Length < right.Length) { - uint[] bits = BigIntegerCalculator.Multiply(right._bits, left._bits); - return new BigInteger(bits, bits, (left._sign < 0) ^ (right._sign < 0)); + Debug.Assert(!left.IsEmpty && !right.IsEmpty); + + int size = left.Length + right.Length; + Span bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); + bits.Clear(); + + BigIntegerCalculator.Multiply(right, left, bits); + result = new BigInteger(bits, (leftSign < 0) ^ (rightSign < 0)); } else { - uint[] bits = BigIntegerCalculator.Multiply(left._bits, right._bits); - return new BigInteger(bits, bits, (left._sign < 0) ^ (right._sign < 0)); + Debug.Assert(!left.IsEmpty && !right.IsEmpty); + + int size = left.Length + right.Length; + Span bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); + bits.Clear(); + + BigIntegerCalculator.Multiply(left, right, bits); + result = new BigInteger(bits, (leftSign < 0) ^ (rightSign < 0)); } + + if (bitsFromPool != null) + ArrayPool.Shared.Return(bitsFromPool); + + return result; } public static BigInteger operator /(BigInteger dividend, BigInteger divisor) @@ -2180,11 +2418,28 @@ stackalloc uint[StackallocUInt32Limit].Slice(0, zl) : return s_bnZeroInt; } + uint[]? quotientFromPool = null; + if (trivialDivisor) { Debug.Assert(dividend._bits != null); - uint[] bits = BigIntegerCalculator.Divide(dividend._bits, NumericsHelpers.Abs(divisor._sign)); - return new BigInteger(bits, bits, (dividend._sign < 0) ^ (divisor._sign < 0)); + + int size = dividend._bits.Length; + Span quotient = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : quotientFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); + + try + { + //may throw DivideByZeroException + BigIntegerCalculator.Divide(dividend._bits, NumericsHelpers.Abs(divisor._sign), quotient); + return new BigInteger(quotient, (dividend._sign < 0) ^ (divisor._sign < 0)); + } + finally + { + if (quotientFromPool != null) + ArrayPool.Shared.Return(quotientFromPool); + } } Debug.Assert(dividend._bits != null && divisor._bits != null); @@ -2195,8 +2450,18 @@ stackalloc uint[StackallocUInt32Limit].Slice(0, zl) : } else { - uint[] bits = BigIntegerCalculator.Divide(dividend._bits, divisor._bits); - return new BigInteger(bits, bits, (dividend._sign < 0) ^ (divisor._sign < 0)); + int size = dividend._bits.Length - divisor._bits.Length + 1; + Span quotient = ((uint)size < BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : quotientFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); + + BigIntegerCalculator.Divide(dividend._bits, divisor._bits, quotient); + var result = new BigInteger(quotient, (dividend._sign < 0) ^ (divisor._sign < 0)); + + if (quotientFromPool != null) + ArrayPool.Shared.Return(quotientFromPool); + + return result; } } @@ -2233,8 +2498,20 @@ stackalloc uint[StackallocUInt32Limit].Slice(0, zl) : { return dividend; } - uint[] bits = BigIntegerCalculator.Remainder(dividend._bits, divisor._bits); - return new BigInteger(bits, bits, dividend._sign < 0); + + uint[]? bitsFromPool = null; + int size = dividend._bits.Length; + Span bits = (size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); + + BigIntegerCalculator.Remainder(dividend._bits, divisor._bits, bits); + var result = new BigInteger(bits, dividend._sign < 0); + + if (bitsFromPool != null) + ArrayPool.Shared.Return(bitsFromPool); + + return result; } public static bool operator <(BigInteger left, BigInteger right) @@ -2451,28 +2728,26 @@ public long GetBitLength() /// Encapsulate the logic of normalizing the "small" and "large" forms of BigInteger /// into the "large" form so that Bit Manipulation algorithms can be simplified. /// - /// /// - /// The UInt32 span containing the entire big integer in "large" (denormalized) form. + /// The UInt32 array containing the entire big integer in "large" (denormalized) form. /// E.g., the number one (1) and negative one (-1) are both stored as 0x00000001 /// BigInteger values Int32.MinValue < x <= Int32.MaxValue are converted to this - /// format for convenience. Expecting to be passed a writeable span of length 1. + /// format for convenience. /// /// True for negative numbers. - private static bool GetPartsForBitManipulation(ref BigInteger x, ref Span xd) + private bool GetPartsForBitManipulation(Span xd) { - Debug.Assert(xd.Length == 1); + Debug.Assert(_bits is null ? xd.Length == 1 : xd.Length == _bits.Length); - if (x._bits == null) + if (_bits is null) { - xd[0] = (uint)(x._sign < 0 ? -x._sign : x._sign); + xd[0] = (uint)(_sign < 0 ? -_sign : _sign); } else { - xd = x._bits; + _bits.CopyTo(xd); } - - return x._sign < 0; + return _sign < 0; } internal static int GetDiffLength(uint[] rgu1, uint[] rgu2, int cu) @@ -2506,4 +2781,4 @@ private void AssertValid() } } } -} +} \ No newline at end of file diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs index 4439d67346de6..c2ac712ec9e67 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs @@ -2,112 +2,89 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; -using System.Security; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace System.Numerics { internal static partial class BigIntegerCalculator { - public static uint[] Add(uint[] left, uint right) + public static void Add(ReadOnlySpan left, uint right, Span bits) { - Debug.Assert(left != null); Debug.Assert(left.Length >= 1); + Debug.Assert(bits.Length == left.Length + 1); // Executes the addition for one big and one 32-bit integer. // Thus, we've similar code than below, but there is no loop for // processing the 32-bit integer, since it's a single element. - uint[] bits = new uint[left.Length + 1]; + long carry = right; - long digit = (long)left[0] + right; - bits[0] = unchecked((uint)digit); - long carry = digit >> 32; - - for (int i = 1; i < left.Length; i++) + for (int i = 0; i < left.Length; i++) { - digit = left[i] + carry; + long digit = left[i] + carry; bits[i] = unchecked((uint)digit); carry = digit >> 32; } - bits[left.Length] = (uint)carry; - return bits; + bits[left.Length] = (uint)carry; } - public static unsafe uint[] Add(uint[] left, uint[] right) + public static void Add(ReadOnlySpan left, ReadOnlySpan right, Span bits) { - Debug.Assert(left != null); - Debug.Assert(right != null); Debug.Assert(left.Length >= right.Length); + Debug.Assert(bits.Length == left.Length + 1); - // Switching to unsafe pointers helps sparing - // some nasty index calculations... - - uint[] bits = new uint[left.Length + 1]; - - fixed (uint* l = left, r = right, b = &bits[0]) - { - Add(l, left.Length, - r, right.Length, - b, bits.Length); - } - - return bits; - } + int i = 0; + long carry = 0L; - private static unsafe void Add(uint* left, int leftLength, - uint* right, int rightLength, - uint* bits, int bitsLength) - { - Debug.Assert(leftLength >= 0); - Debug.Assert(rightLength >= 0); - Debug.Assert(leftLength >= rightLength); - Debug.Assert(bitsLength == leftLength + 1); + // Switching to managed references helps eliminating + // index bounds check... + ref uint leftPtr = ref MemoryMarshal.GetReference(left); + ref uint resultPtr = ref MemoryMarshal.GetReference(bits); // Executes the "grammar-school" algorithm for computing z = a + b. // While calculating z_i = a_i + b_i we take care of overflow: // Since a_i + b_i + c <= 2(2^32 - 1) + 1 = 2^33 - 1, our carry c // has always the value 1 or 0; hence, we're safe here. - int i = 0; - long carry = 0L; - - for (; i < rightLength; i++) + for ( ; i < right.Length; i++) { - long digit = (left[i] + carry) + right[i]; - bits[i] = unchecked((uint)digit); + long digit = (Unsafe.Add(ref leftPtr, i) + carry) + right[i]; + Unsafe.Add(ref resultPtr, i) = unchecked((uint)digit); carry = digit >> 32; } - for (; i < leftLength; i++) + for ( ; i < left.Length; i++) { long digit = left[i] + carry; - bits[i] = unchecked((uint)digit); + Unsafe.Add(ref resultPtr, i) = unchecked((uint)digit); carry = digit >> 32; } - bits[i] = (uint)carry; + Unsafe.Add(ref resultPtr, i) = (uint)carry; } - private static unsafe void AddSelf(uint* left, int leftLength, - uint* right, int rightLength) + private static void AddSelf(Span left, ReadOnlySpan right) { - Debug.Assert(leftLength >= 0); - Debug.Assert(rightLength >= 0); - Debug.Assert(leftLength >= rightLength); + Debug.Assert(left.Length >= right.Length); + + int i = 0; + long carry = 0L; + + // Switching to managed references helps eliminating + // index bounds check... + ref uint leftPtr = ref MemoryMarshal.GetReference(left); // Executes the "grammar-school" algorithm for computing z = a + b. // Same as above, but we're writing the result directly to a and // stop execution, if we're out of b and c is already 0. - int i = 0; - long carry = 0L; - - for (; i < rightLength; i++) + for ( ; i < right.Length; i++) { - long digit = (left[i] + carry) + right[i]; - left[i] = unchecked((uint)digit); + long digit = (Unsafe.Add(ref leftPtr, i) + carry) + right[i]; + Unsafe.Add(ref leftPtr, i) = unchecked((uint)digit); carry = digit >> 32; } - for (; carry != 0 && i < leftLength; i++) + for ( ; carry != 0 && i < left.Length; i++) { long digit = left[i] + carry; left[i] = (uint)digit; @@ -117,110 +94,84 @@ private static unsafe void AddSelf(uint* left, int leftLength, Debug.Assert(carry == 0); } - public static uint[] Subtract(uint[] left, uint right) + public static void Subtract(ReadOnlySpan left, uint right, Span bits) { - Debug.Assert(left != null); Debug.Assert(left.Length >= 1); Debug.Assert(left[0] >= right || left.Length >= 2); + Debug.Assert(bits.Length == left.Length); // Executes the subtraction for one big and one 32-bit integer. // Thus, we've similar code than below, but there is no loop for // processing the 32-bit integer, since it's a single element. - uint[] bits = new uint[left.Length]; + long carry = -right; - long digit = (long)left[0] - right; - bits[0] = unchecked((uint)digit); - long carry = digit >> 32; - - for (int i = 1; i < left.Length; i++) + for (int i = 0; i < left.Length; i++) { - digit = left[i] + carry; + long digit = left[i] + carry; bits[i] = unchecked((uint)digit); carry = digit >> 32; } - - return bits; } - public static unsafe uint[] Subtract(uint[] left, uint[] right) + public static void Subtract(ReadOnlySpan left, ReadOnlySpan right, Span bits) { - Debug.Assert(left != null); - Debug.Assert(right != null); Debug.Assert(left.Length >= right.Length); Debug.Assert(Compare(left, right) >= 0); + Debug.Assert(bits.Length == left.Length); - // Switching to unsafe pointers helps sparing - // some nasty index calculations... - - uint[] bits = new uint[left.Length]; - - fixed (uint* l = left, r = right, b = bits) - { - Subtract(l, left.Length, - r, right.Length, - b, bits.Length); - } - - return bits; - } + int i = 0; + long carry = 0L; - private static unsafe void Subtract(uint* left, int leftLength, - uint* right, int rightLength, - uint* bits, int bitsLength) - { - Debug.Assert(leftLength >= 0); - Debug.Assert(rightLength >= 0); - Debug.Assert(leftLength >= rightLength); - Debug.Assert(Compare(left, leftLength, right, rightLength) >= 0); - Debug.Assert(bitsLength == leftLength); + // Switching to managed references helps eliminating + // index bounds check... + ref uint leftPtr = ref MemoryMarshal.GetReference(left); + ref uint resultPtr = ref MemoryMarshal.GetReference(bits); // Executes the "grammar-school" algorithm for computing z = a - b. // While calculating z_i = a_i - b_i we take care of overflow: // Since a_i - b_i doesn't need any additional bit, our carry c // has always the value -1 or 0; hence, we're safe here. - int i = 0; - long carry = 0L; - - for (; i < rightLength; i++) + for ( ; i < right.Length; i++) { - long digit = (left[i] + carry) - right[i]; - bits[i] = unchecked((uint)digit); + long digit = (Unsafe.Add(ref leftPtr, i) + carry) - right[i]; + Unsafe.Add(ref resultPtr, i) = unchecked((uint)digit); carry = digit >> 32; } - for (; i < leftLength; i++) + for ( ; i < left.Length; i++) { long digit = left[i] + carry; - bits[i] = (uint)digit; + Unsafe.Add(ref resultPtr, i) = (uint)digit; carry = digit >> 32; } Debug.Assert(carry == 0); } - private static unsafe void SubtractSelf(uint* left, int leftLength, - uint* right, int rightLength) + private static void SubtractSelf(Span left, ReadOnlySpan right) { - Debug.Assert(leftLength >= 0); - Debug.Assert(rightLength >= 0); - Debug.Assert(leftLength >= rightLength); - Debug.Assert(Compare(left, leftLength, right, rightLength) >= 0); + Debug.Assert(left.Length >= right.Length); + Debug.Assert(Compare(left, right) >= 0); + + int i = 0; + long carry = 0L; + + // Switching to managed references helps eliminating + // index bounds check... + ref uint leftPtr = ref MemoryMarshal.GetReference(left); // Executes the "grammar-school" algorithm for computing z = a - b. // Same as above, but we're writing the result directly to a and // stop execution, if we're out of b and c is already 0. - int i = 0; - long carry = 0L; - - for (; i < rightLength; i++) + for (; i < right.Length; i++) { - long digit = (left[i] + carry) - right[i]; - left[i] = unchecked((uint)digit); + long digit = (Unsafe.Add(ref leftPtr, i) + carry) - right[i]; + Unsafe.Add(ref leftPtr, i) = unchecked((uint)digit); carry = digit >> 32; } - for (; carry != 0 && i < leftLength; i++) + for (; carry != 0 && i < left.Length; i++) { long digit = left[i] + carry; left[i] = (uint)digit; @@ -229,48 +180,5 @@ private static unsafe void SubtractSelf(uint* left, int leftLength, Debug.Assert(carry == 0); } - - public static int Compare(uint[] left, uint[] right) - { - Debug.Assert(left != null); - Debug.Assert(right != null); - - if (left.Length < right.Length) - return -1; - if (left.Length > right.Length) - return 1; - - for (int i = left.Length - 1; i >= 0; i--) - { - if (left[i] < right[i]) - return -1; - if (left[i] > right[i]) - return 1; - } - - return 0; - } - - private static unsafe int Compare(uint* left, int leftLength, - uint* right, int rightLength) - { - Debug.Assert(leftLength >= 0); - Debug.Assert(rightLength >= 0); - - if (leftLength < rightLength) - return -1; - if (leftLength > rightLength) - return 1; - - for (int i = leftLength - 1; i >= 0; i--) - { - if (left[i] < right[i]) - return -1; - if (left[i] > right[i]) - return 1; - } - - return 0; - } } } diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs deleted file mode 100644 index ec05118f03007..0000000000000 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs +++ /dev/null @@ -1,215 +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.Diagnostics; -using System.Security; - -namespace System.Numerics -{ - // ATTENTION: always pass BitsBuffer by reference, - // it's a structure for performance reasons. Furthermore - // it's a mutable one, so use it only with care! - - internal static partial class BigIntegerCalculator - { - // To spare memory allocations a buffer helps reusing memory! - // We just create the target array twice and switch between every - // operation. In order to not compute unnecessarily with all those - // leading zeros we take care of the current actual length. - - internal struct BitsBuffer - { - private uint[] _bits; - private int _length; - - public BitsBuffer(int size, uint value) - { - Debug.Assert(size >= 1); - - _bits = new uint[size]; - _length = value != 0 ? 1 : 0; - - _bits[0] = value; - } - - public BitsBuffer(int size, uint[] value) - { - Debug.Assert(value != null); - Debug.Assert(size >= ActualLength(value)); - - _bits = new uint[size]; - _length = ActualLength(value); - - Array.Copy(value, _bits, _length); - } - - public unsafe void MultiplySelf(ref BitsBuffer value, - ref BitsBuffer temp) - { - Debug.Assert(temp._length == 0); - Debug.Assert(_length + value._length <= temp._bits.Length); - - // Executes a multiplication for this and value, writes the - // result to temp. Switches this and temp arrays afterwards. - - fixed (uint* b = _bits, v = value._bits, t = temp._bits) - { - if (_length < value._length) - { - Multiply(v, value._length, - b, _length, - t, _length + value._length); - } - else - { - Multiply(b, _length, - v, value._length, - t, _length + value._length); - } - } - - Apply(ref temp, _length + value._length); - } - - public unsafe void SquareSelf(ref BitsBuffer temp) - { - Debug.Assert(temp._length == 0); - Debug.Assert(_length + _length <= temp._bits.Length); - - // Executes a square for this, writes the result to temp. - // Switches this and temp arrays afterwards. - - fixed (uint* b = _bits, t = temp._bits) - { - Square(b, _length, - t, _length + _length); - } - - Apply(ref temp, _length + _length); - } - - public void Reduce(ref FastReducer reducer) - { - // Executes a modulo operation using an optimized reducer. - // Thus, no need of any switching here, happens in-line. - - _length = reducer.Reduce(_bits, _length); - } - - public unsafe void Reduce(uint[] modulus) - { - Debug.Assert(modulus != null); - - // Executes a modulo operation using the divide operation. - // Thus, no need of any switching here, happens in-line. - - if (_length >= modulus.Length) - { - fixed (uint* b = _bits, m = modulus) - { - Divide(b, _length, - m, modulus.Length, - null, 0); - } - - _length = ActualLength(_bits, modulus.Length); - } - } - - public unsafe void Reduce(ref BitsBuffer modulus) - { - // Executes a modulo operation using the divide operation. - // Thus, no need of any switching here, happens in-line. - - if (_length >= modulus._length) - { - fixed (uint* b = _bits, m = modulus._bits) - { - Divide(b, _length, - m, modulus._length, - null, 0); - } - - _length = ActualLength(_bits, modulus._length); - } - } - - public void Overwrite(ulong value) - { - Debug.Assert(_bits.Length >= 2); - - if (_length > 2) - { - // Ensure leading zeros - Array.Clear(_bits, 2, _length - 2); - } - - uint lo = unchecked((uint)value); - uint hi = (uint)(value >> 32); - - _bits[0] = lo; - _bits[1] = hi; - _length = hi != 0 ? 2 : lo != 0 ? 1 : 0; - } - - public void Overwrite(uint value) - { - Debug.Assert(_bits.Length >= 1); - - if (_length > 1) - { - // Ensure leading zeros - Array.Clear(_bits, 1, _length - 1); - } - - _bits[0] = value; - _length = value != 0 ? 1 : 0; - } - - public uint[] GetBits() - { - return _bits; - } - - public int GetSize() - { - return _bits.Length; - } - - public int GetLength() - { - return _length; - } - - public void Refresh(int maxLength) - { - Debug.Assert(_bits.Length >= maxLength); - - if (_length > maxLength) - { - // Ensure leading zeros - Array.Clear(_bits, maxLength, _length - maxLength); - } - - _length = ActualLength(_bits, maxLength); - } - - private void Apply(ref BitsBuffer temp, int maxLength) - { - Debug.Assert(temp._length == 0); - Debug.Assert(maxLength <= temp._bits.Length); - - // Resets this and switches this and temp afterwards. - // The caller assumed an empty temp, the next will too. - - Array.Clear(_bits, 0, _length); - - uint[] t = temp._bits; - temp._bits = _bits; - _bits = t; - - _length = ActualLength(_bits, maxLength); - } - } - } -} diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs index bb75e77a228d0..9ca65e0813e25 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs @@ -1,26 +1,24 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Buffers; using System.Diagnostics; -using System.Security; namespace System.Numerics { internal static partial class BigIntegerCalculator { - public static uint[] Divide(uint[] left, uint right, - out uint remainder) + public static void Divide(ReadOnlySpan left, uint right, Span quotient, out uint remainder) { - Debug.Assert(left != null); Debug.Assert(left.Length >= 1); + Debug.Assert(quotient.Length == left.Length); // Executes the division for one big and one 32-bit integer. // Thus, we've similar code than below, but there is no loop for // processing the 32-bit integer, since it's a single element. - uint[] quotient = new uint[left.Length]; - ulong carry = 0UL; + for (int i = left.Length - 1; i >= 0; i--) { ulong value = (carry << 32) | left[i]; @@ -29,19 +27,15 @@ public static uint[] Divide(uint[] left, uint right, carry = value - digit * right; } remainder = (uint)carry; - - return quotient; } - public static uint[] Divide(uint[] left, uint right) + public static void Divide(ReadOnlySpan left, uint right, Span quotient) { - Debug.Assert(left != null); Debug.Assert(left.Length >= 1); + Debug.Assert(quotient.Length == left.Length); // Same as above, but only computing the quotient. - uint[] quotient = new uint[left.Length]; - ulong carry = 0UL; for (int i = left.Length - 1; i >= 0; i--) { @@ -50,17 +44,13 @@ public static uint[] Divide(uint[] left, uint right) quotient[i] = (uint)digit; carry = value - digit * right; } - - return quotient; } - public static uint Remainder(uint[] left, uint right) + public static uint Remainder(ReadOnlySpan left, uint right) { - Debug.Assert(left != null); Debug.Assert(left.Length >= 1); // Same as above, but only computing the remainder. - ulong carry = 0UL; for (int i = left.Length - 1; i >= 0; i--) { @@ -71,115 +61,79 @@ public static uint Remainder(uint[] left, uint right) return (uint)carry; } - public static unsafe uint[] Divide(uint[] left, uint[] right, - out uint[] remainder) + public static void Divide(ReadOnlySpan left, ReadOnlySpan right, Span quotient, Span remainder) { - Debug.Assert(left != null); - Debug.Assert(right != null); Debug.Assert(left.Length >= 1); Debug.Assert(right.Length >= 1); Debug.Assert(left.Length >= right.Length); + Debug.Assert(quotient.Length == left.Length - right.Length + 1); + Debug.Assert(remainder.Length == left.Length); - // Switching to unsafe pointers helps sparing - // some nasty index calculations... - - uint[] localLeft = left.AsSpan().ToArray(); // left will get overwritten, we need a local copy - uint[] bits = new uint[left.Length - right.Length + 1]; - - fixed (uint* l = &localLeft[0], r = &right[0], b = &bits[0]) - { - Divide(l, localLeft.Length, - r, right.Length, - b, bits.Length); - } - - remainder = localLeft; - - return bits; + left.CopyTo(remainder); + Divide(remainder, right, quotient); } - public static unsafe uint[] Divide(uint[] left, uint[] right) + public static void Divide(ReadOnlySpan left, ReadOnlySpan right, Span quotient) { - Debug.Assert(left != null); - Debug.Assert(right != null); Debug.Assert(left.Length >= 1); Debug.Assert(right.Length >= 1); Debug.Assert(left.Length >= right.Length); + Debug.Assert(quotient.Length == left.Length - right.Length + 1); // Same as above, but only returning the quotient. - // left will get overwritten, we need a local copy - Span localLeft = stackalloc uint[0]; - if (left.Length <= BigInteger.StackallocUInt32Limit) - { - localLeft = stackalloc uint[BigInteger.StackallocUInt32Limit].Slice(0, left.Length); - left.AsSpan().CopyTo(localLeft); - } - else - { - localLeft = left.AsSpan().ToArray(); - } + uint[]? leftCopyFromPool = null; - uint[] bits = new uint[left.Length - right.Length + 1]; + // NOTE: left will get overwritten, we need a local copy + // However, mutated left is not used afterwards, so use array pooling or stack alloc + Span leftCopy = (left.Length <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : leftCopyFromPool = ArrayPool.Shared.Rent(left.Length)).Slice(0, left.Length); + left.CopyTo(leftCopy); - fixed (uint* l = &localLeft[0], r = &right[0], b = &bits[0]) - { - Divide(l, localLeft.Length, - r, right.Length, - b, bits.Length); - } + Divide(leftCopy, right, quotient); - return bits; + if (leftCopyFromPool != null) + ArrayPool.Shared.Return(leftCopyFromPool); } - public static unsafe uint[] Remainder(uint[] left, uint[] right) + public static void Remainder(ReadOnlySpan left, ReadOnlySpan right, Span remainder) { - Debug.Assert(left != null); - Debug.Assert(right != null); Debug.Assert(left.Length >= 1); Debug.Assert(right.Length >= 1); Debug.Assert(left.Length >= right.Length); + Debug.Assert(remainder.Length >= left.Length); // Same as above, but only returning the remainder. - uint[] localLeft = left.AsSpan().ToArray(); // left will get overwritten, we need a local copy - - fixed (uint* l = &localLeft[0], r = &right[0]) - { - Divide(l, localLeft.Length, - r, right.Length, - null, 0); - } - - return localLeft; + left.CopyTo(remainder); + Divide(remainder, right, default); } - private static unsafe void Divide(uint* left, int leftLength, - uint* right, int rightLength, - uint* bits, int bitsLength) + private static void Divide(Span left, ReadOnlySpan right, Span bits) { - Debug.Assert(leftLength >= 1); - Debug.Assert(rightLength >= 1); - Debug.Assert(leftLength >= rightLength); - Debug.Assert(bitsLength == leftLength - rightLength + 1 - || bitsLength == 0); + Debug.Assert(left.Length >= 1); + Debug.Assert(right.Length >= 1); + Debug.Assert(left.Length >= right.Length); + Debug.Assert(bits.Length == left.Length - right.Length + 1 + || bits.Length == 0); // Executes the "grammar-school" algorithm for computing q = a / b. // Before calculating q_i, we get more bits into the highest bit // block of the divisor. Thus, guessing digits of the quotient // will be more precise. Additionally we'll get r = a % b. - uint divHi = right[rightLength - 1]; - uint divLo = rightLength > 1 ? right[rightLength - 2] : 0; + uint divHi = right[right.Length - 1]; + uint divLo = right.Length > 1 ? right[right.Length - 2] : 0; // We measure the leading zeros of the divisor - int shift = LeadingZeros(divHi); + int shift = BitOperations.LeadingZeroCount(divHi); int backShift = 32 - shift; // And, we make sure the most significant bit is set if (shift > 0) { - uint divNx = rightLength > 2 ? right[rightLength - 3] : 0; + uint divNx = right.Length > 2 ? right[right.Length - 3] : 0; divHi = (divHi << shift) | (divLo >> backShift); divLo = (divLo << shift) | (divNx >> backShift); @@ -187,10 +141,10 @@ private static unsafe void Divide(uint* left, int leftLength, // Then, we divide all of the bits as we would do it using // pen and paper: guessing the next digit, subtracting, ... - for (int i = leftLength; i >= rightLength; i--) + for (int i = left.Length; i >= right.Length; i--) { - int n = i - rightLength; - uint t = i < leftLength ? left[i] : 0; + int n = i - right.Length; + uint t = (uint)i < (uint)left.Length ? left[i] : 0; ulong valHi = ((ulong)t << 32) | left[i - 1]; uint valLo = i > 1 ? left[i - 2] : 0; @@ -217,15 +171,13 @@ private static unsafe void Divide(uint* left, int leftLength, if (digit > 0) { // Now it's time to subtract our current quotient - uint carry = SubtractDivisor(left + n, leftLength - n, - right, rightLength, digit); + uint carry = SubtractDivisor(left.Slice(n), right, digit); if (carry != t) { Debug.Assert(carry == t + 1); // Our guess was still exactly one too high - carry = AddDivisor(left + n, leftLength - n, - right, rightLength); + carry = AddDivisor(left.Slice(n), right); --digit; Debug.Assert(carry == 1); @@ -233,41 +185,36 @@ private static unsafe void Divide(uint* left, int leftLength, } // We have the digit! - if (bitsLength != 0) + if ((uint)n < (uint)bits.Length) bits[n] = (uint)digit; - if (i < leftLength) + + if ((uint)i < (uint)left.Length) left[i] = 0; } } - private static unsafe uint AddDivisor(uint* left, int leftLength, - uint* right, int rightLength) + private static uint AddDivisor(Span left, ReadOnlySpan right) { - Debug.Assert(leftLength >= 0); - Debug.Assert(rightLength >= 0); - Debug.Assert(leftLength >= rightLength); + Debug.Assert(left.Length >= right.Length); // Repairs the dividend, if the last subtract was too much ulong carry = 0UL; - for (int i = 0; i < rightLength; i++) + for (int i = 0; i < right.Length; i++) { - ulong digit = (left[i] + carry) + right[i]; - left[i] = unchecked((uint)digit); + ref uint leftElement = ref left[i]; + ulong digit = (leftElement + carry) + right[i]; + leftElement = unchecked((uint)digit); carry = digit >> 32; } return (uint)carry; } - private static unsafe uint SubtractDivisor(uint* left, int leftLength, - uint* right, int rightLength, - ulong q) + private static uint SubtractDivisor(Span left, ReadOnlySpan right, ulong q) { - Debug.Assert(leftLength >= 0); - Debug.Assert(rightLength >= 0); - Debug.Assert(leftLength >= rightLength); + Debug.Assert(left.Length >= right.Length); Debug.Assert(q <= 0xFFFFFFFF); // Combines a subtract and a multiply operation, which is naturally @@ -275,14 +222,15 @@ private static unsafe uint SubtractDivisor(uint* left, int leftLength, ulong carry = 0UL; - for (int i = 0; i < rightLength; i++) + for (int i = 0; i < right.Length; i++) { carry += right[i] * q; uint digit = unchecked((uint)carry); carry = carry >> 32; - if (left[i] < digit) + ref uint leftElement = ref left[i]; + if (leftElement < digit) ++carry; - left[i] = unchecked(left[i] - digit); + leftElement = unchecked(leftElement - digit); } return (uint)carry; @@ -316,39 +264,5 @@ private static bool DivideGuessTooBig(ulong q, ulong valHi, uint valLo, return false; } - - private static int LeadingZeros(uint value) - { - if (value == 0) - return 32; - - int count = 0; - if ((value & 0xFFFF0000) == 0) - { - count += 16; - value = value << 16; - } - if ((value & 0xFF000000) == 0) - { - count += 8; - value = value << 8; - } - if ((value & 0xF0000000) == 0) - { - count += 4; - value = value << 4; - } - if ((value & 0xC0000000) == 0) - { - count += 2; - value = value << 2; - } - if ((value & 0x80000000) == 0) - { - count += 1; - } - - return count; - } } } diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs index 8731794b40299..0b56d2b52e245 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; -using System.Security; namespace System.Numerics { @@ -14,135 +13,114 @@ internal static partial class BigIntegerCalculator // see https://en.wikipedia.org/wiki/Barrett_reduction - internal readonly struct FastReducer + private readonly ref struct FastReducer { - private readonly uint[] _modulus; - private readonly uint[] _mu; - private readonly uint[] _q1; - private readonly uint[] _q2; + private readonly ReadOnlySpan _modulus; + private readonly ReadOnlySpan _mu; + private readonly Span _q1; + private readonly Span _q2; - private readonly int _muLength; - - public FastReducer(uint[] modulus) + public FastReducer(ReadOnlySpan modulus, Span r, Span mu, Span q1, Span q2) { - Debug.Assert(modulus != null); + Debug.Assert(!modulus.IsEmpty); + Debug.Assert(r.Length == modulus.Length * 2 + 1); + Debug.Assert(mu.Length == r.Length - modulus.Length + 1); + Debug.Assert(q1.Length == modulus.Length * 2 + 2); + Debug.Assert(q2.Length == modulus.Length * 2 + 2); // Let r = 4^k, with 2^k > m - uint[] r = new uint[modulus.Length * 2 + 1]; r[r.Length - 1] = 1; // Let mu = 4^k / m - _mu = Divide(r, modulus); + Divide(r, modulus, mu); _modulus = modulus; - // Allocate memory for quotients once - _q1 = new uint[modulus.Length * 2 + 2]; - _q2 = new uint[modulus.Length * 2 + 1]; + _q1 = q1; + _q2 = q2; - _muLength = ActualLength(_mu); + _mu = mu.Slice(0, ActualLength(mu)); } - public int Reduce(uint[] value, int length) + public int Reduce(Span value) { - Debug.Assert(value != null); - Debug.Assert(length <= value.Length); Debug.Assert(value.Length <= _modulus.Length * 2); // Trivial: value is shorter - if (length < _modulus.Length) - return length; + if (value.Length < _modulus.Length) + return value.Length; // Let q1 = v/2^(k-1) * mu - int l1 = DivMul(value, length, _mu, _muLength, - _q1, _modulus.Length - 1); + _q1.Clear(); + int l1 = DivMul(value, _mu, _q1, _modulus.Length - 1); // Let q2 = q1/2^(k+1) * m - int l2 = DivMul(_q1, l1, _modulus, _modulus.Length, - _q2, _modulus.Length + 1); + _q2.Clear(); + int l2 = DivMul(_q1.Slice(0, l1), _modulus, _q2, _modulus.Length + 1); // Let v = (v - q2) % 2^(k+1) - i*m - return SubMod(value, length, _q2, l2, - _modulus, _modulus.Length + 1); + var length = SubMod(value, _q2.Slice(0, l2), _modulus, _modulus.Length + 1); + value = value.Slice(length); + value.Clear(); + + return length; } - private static unsafe int DivMul(uint[] left, int leftLength, - uint[] right, int rightLength, - uint[] bits, int k) + private static int DivMul(ReadOnlySpan left, ReadOnlySpan right, Span bits, int k) { - Debug.Assert(left != null); - Debug.Assert(left.Length >= leftLength); - Debug.Assert(right != null); - Debug.Assert(right.Length >= rightLength); - Debug.Assert(bits != null); - Debug.Assert(bits.Length + k >= leftLength + rightLength); + Debug.Assert(!right.IsEmpty); + Debug.Assert(!bits.IsEmpty); + Debug.Assert(bits.Length + k >= left.Length + right.Length); // Executes the multiplication algorithm for left and right, // but skips the first k limbs of left, which is equivalent to // preceding division by 2^(32*k). To spare memory allocations // we write the result to an already allocated memory. - Array.Clear(bits); - - if (leftLength > k) + if (left.Length > k) { - leftLength -= k; + left = left.Slice(k); - fixed (uint* l = left, r = right, b = bits) + if (left.Length < right.Length) { - if (leftLength < rightLength) - { - Multiply(r, rightLength, - l + k, leftLength, - b, leftLength + rightLength); - } - else - { - Multiply(l + k, leftLength, - r, rightLength, - b, leftLength + rightLength); - } + Multiply(right, + left, + bits.Slice(0, left.Length + right.Length)); + } + else + { + Multiply(left, + right, + bits.Slice(0, left.Length + right.Length)); } - return ActualLength(bits, leftLength + rightLength); + return ActualLength(bits.Slice(0, left.Length + right.Length)); } return 0; } - private static unsafe int SubMod(uint[] left, int leftLength, - uint[] right, int rightLength, - uint[] modulus, int k) + private static int SubMod(Span left, ReadOnlySpan right, ReadOnlySpan modulus, int k) { - Debug.Assert(left != null); - Debug.Assert(left.Length >= leftLength); - Debug.Assert(right != null); - Debug.Assert(right.Length >= rightLength); - // Executes the subtraction algorithm for left and right, // but considers only the first k limbs, which is equivalent to // preceding reduction by 2^(32*k). Furthermore, if left is // still greater than modulus, further subtractions are used. - if (leftLength > k) - leftLength = k; - if (rightLength > k) - rightLength = k; + if (left.Length > k) + left = left.Slice(0, k); + if (right.Length > k) + right = right.Slice(0, k); - fixed (uint* l = left, r = right, m = modulus) - { - SubtractSelf(l, leftLength, r, rightLength); - leftLength = ActualLength(left, leftLength); + SubtractSelf(left, right); + left = left.Slice(0, ActualLength(left)); - while (Compare(l, leftLength, m, modulus.Length) >= 0) - { - SubtractSelf(l, leftLength, m, modulus.Length); - leftLength = ActualLength(left, leftLength); - } + while (Compare(left, modulus) >= 0) + { + SubtractSelf(left, modulus); + left = left.Slice(0, ActualLength(left)); } - Array.Clear(left, leftLength, left.Length - leftLength); - - return leftLength; + return left.Length; } } } diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs index d984912ec6246..c9944c1ba95bb 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Buffers; using System.Diagnostics; namespace System.Numerics @@ -40,9 +41,8 @@ public static ulong Gcd(ulong left, ulong right) return left; } - public static uint Gcd(uint[] left, uint right) + public static uint Gcd(ReadOnlySpan left, uint right) { - Debug.Assert(left != null); Debug.Assert(left.Length >= 1); Debug.Assert(right != 0); @@ -54,27 +54,34 @@ public static uint Gcd(uint[] left, uint right) return Gcd(right, temp); } - public static uint[] Gcd(uint[] left, uint[] right) + public static void Gcd(ReadOnlySpan left, ReadOnlySpan right, Span result) { - Debug.Assert(left != null); Debug.Assert(left.Length >= 2); - Debug.Assert(right != null); Debug.Assert(right.Length >= 2); Debug.Assert(Compare(left, right) >= 0); + Debug.Assert(result.Length == left.Length); - BitsBuffer leftBuffer = new BitsBuffer(left.Length, left); - BitsBuffer rightBuffer = new BitsBuffer(right.Length, right); + left.CopyTo(result); - Gcd(ref leftBuffer, ref rightBuffer); + uint[]? rightCopyFromPool = null; + Span rightCopy = (right.Length <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : rightCopyFromPool = ArrayPool.Shared.Rent(right.Length)).Slice(0, right.Length); + right.CopyTo(rightCopy); - return leftBuffer.GetBits(); + Gcd(result, rightCopy); + + if (rightCopyFromPool != null) + ArrayPool.Shared.Return(rightCopyFromPool); } - private static void Gcd(ref BitsBuffer left, ref BitsBuffer right) + private static void Gcd(Span left, Span right) { - Debug.Assert(left.GetLength() >= 2); - Debug.Assert(right.GetLength() >= 2); - Debug.Assert(left.GetLength() >= right.GetLength()); + Debug.Assert(left.Length >= 2); + Debug.Assert(right.Length >= 2); + Debug.Assert(left.Length >= right.Length); + + Span result = left; //keep result buffer untouched during computation // Executes Lehmer's gcd algorithm, but uses the most // significant bits to work with 64-bit (not 32-bit) values. @@ -83,11 +90,11 @@ private static void Gcd(ref BitsBuffer left, ref BitsBuffer right) // http://cacr.uwaterloo.ca/hac/about/chap14.pdf (see 14.4.2) // ftp://ftp.risc.uni-linz.ac.at/pub/techreports/1992/92-69.ps.gz - while (right.GetLength() > 2) + while (right.Length > 2) { ulong x, y; - ExtractDigits(ref left, ref right, out x, out y); + ExtractDigits(left, right, out x, out y); uint a = 1U, b = 0U; uint c = 0U, d = 1U; @@ -149,85 +156,112 @@ private static void Gcd(ref BitsBuffer left, ref BitsBuffer right) if (b == 0) { // Euclid's step - left.Reduce(ref right); + left = left.Slice(0, Reduce(left, right)); - BitsBuffer temp = left; + Span temp = left; left = right; right = temp; } else { // Lehmer's step - LehmerCore(ref left, ref right, a, b, c, d); + var count = LehmerCore(left, right, a, b, c, d); + left = left.Slice(0, Refresh(left, count)); + right = right.Slice(0, Refresh(right, count)); if (iteration % 2 == 1) { // Ensure left is larger than right - BitsBuffer temp = left; + Span temp = left; left = right; right = temp; } } } - if (right.GetLength() > 0) + if (right.Length > 0) { // Euclid's step - left.Reduce(ref right); + Reduce(left, right); - uint[] xBits = right.GetBits(); - uint[] yBits = left.GetBits(); + ulong x = ((ulong)right[1] << 32) | right[0]; + ulong y = ((ulong)left[1] << 32) | left[0]; - ulong x = ((ulong)xBits[1] << 32) | xBits[0]; - ulong y = ((ulong)yBits[1] << 32) | yBits[0]; + left = left.Slice(0, Overwrite(left, Gcd(x, y))); + Overwrite(right, 0U); + } + + left.CopyTo(result); + } - left.Overwrite(Gcd(x, y)); - right.Overwrite(0); + private static int Overwrite(Span buffer, ulong value) + { + Debug.Assert(buffer.Length >= 2); + + if (buffer.Length > 2) + { + // Ensure leading zeros in little-endian + buffer.Slice(2).Clear(); } + + uint lo = unchecked((uint)value); + uint hi = (uint)(value >> 32); + + buffer[1] = hi; + buffer[0] = lo; + return hi != 0 ? 2 : lo != 0 ? 1 : 0; } - private static void ExtractDigits(ref BitsBuffer xBuffer, - ref BitsBuffer yBuffer, + private static int Overwrite(Span bits, uint value) + { + Debug.Assert(bits.Length >= 1); + + if (bits.Length > 1) + { + // Ensure leading zeros in little-endian + bits.Slice(1).Clear(); + } + + bits[0] = value; + return value != 0 ? 1 : 0; + } + + private static void ExtractDigits(ReadOnlySpan xBuffer, + ReadOnlySpan yBuffer, out ulong x, out ulong y) { - Debug.Assert(xBuffer.GetLength() >= 3); - Debug.Assert(yBuffer.GetLength() >= 3); - Debug.Assert(xBuffer.GetLength() >= yBuffer.GetLength()); + Debug.Assert(xBuffer.Length >= 3); + Debug.Assert(yBuffer.Length >= 3); + Debug.Assert(xBuffer.Length >= yBuffer.Length); // Extracts the most significant bits of x and y, // but ensures the quotient x / y does not change! - uint[] xBits = xBuffer.GetBits(); - int xLength = xBuffer.GetLength(); - - uint[] yBits = yBuffer.GetBits(); - int yLength = yBuffer.GetLength(); - - ulong xh = xBits[xLength - 1]; - ulong xm = xBits[xLength - 2]; - ulong xl = xBits[xLength - 3]; + ulong xh = xBuffer[xBuffer.Length - 1]; + ulong xm = xBuffer[xBuffer.Length - 2]; + ulong xl = xBuffer[xBuffer.Length - 3]; ulong yh, ym, yl; // arrange the bits - switch (xLength - yLength) + switch (xBuffer.Length - yBuffer.Length) { case 0: - yh = yBits[yLength - 1]; - ym = yBits[yLength - 2]; - yl = yBits[yLength - 3]; + yh = yBuffer[yBuffer.Length - 1]; + ym = yBuffer[yBuffer.Length - 2]; + yl = yBuffer[yBuffer.Length - 3]; break; case 1: yh = 0UL; - ym = yBits[yLength - 1]; - yl = yBits[yLength - 2]; + ym = yBuffer[yBuffer.Length - 1]; + yl = yBuffer[yBuffer.Length - 2]; break; case 2: yh = 0UL; ym = 0UL; - yl = yBits[yLength - 1]; + yl = yBuffer[yBuffer.Length - 1]; break; default: @@ -238,7 +272,7 @@ private static void ExtractDigits(ref BitsBuffer xBuffer, } // Use all the bits but one, see [hac] 14.58 (ii) - int z = LeadingZeros((uint)xh); + int z = BitOperations.LeadingZeroCount((uint)xh); x = ((xh << 32 + z) | (xm << z) | (xl >> 32 - z)) >> 1; y = ((yh << 32 + z) | (ym << z) | (yl >> 32 - z)) >> 1; @@ -246,23 +280,20 @@ private static void ExtractDigits(ref BitsBuffer xBuffer, Debug.Assert(x >= y); } - private static void LehmerCore(ref BitsBuffer xBuffer, - ref BitsBuffer yBuffer, - long a, long b, - long c, long d) + private static int LehmerCore(Span x, + Span y, + long a, long b, + long c, long d) { - Debug.Assert(xBuffer.GetLength() >= 1); - Debug.Assert(yBuffer.GetLength() >= 1); - Debug.Assert(xBuffer.GetLength() >= yBuffer.GetLength()); + Debug.Assert(x.Length >= 1); + Debug.Assert(y.Length >= 1); + Debug.Assert(x.Length >= y.Length); Debug.Assert(a <= 0x7FFFFFFF && b <= 0x7FFFFFFF); Debug.Assert(c <= 0x7FFFFFFF && d <= 0x7FFFFFFF); // Executes the combined calculation of Lehmer's step. - uint[] x = xBuffer.GetBits(); - uint[] y = yBuffer.GetBits(); - - int length = yBuffer.GetLength(); + int length = y.Length; long xCarry = 0L, yCarry = 0L; for (int i = 0; i < length; i++) @@ -275,8 +306,20 @@ private static void LehmerCore(ref BitsBuffer xBuffer, y[i] = unchecked((uint)yDigit); } - xBuffer.Refresh(length); - yBuffer.Refresh(length); + return length; + } + + private static int Refresh(Span bits, int maxLength) + { + Debug.Assert(bits.Length >= maxLength); + + if (bits.Length > maxLength) + { + // Ensure leading zeros + bits.Slice(maxLength).Clear(); + } + + return ActualLength(bits.Slice(0, maxLength)); } } } diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs index c646173430275..0407feff54fe3 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Buffers; using System.Diagnostics; namespace System.Numerics @@ -12,50 +13,104 @@ internal static partial class BigIntegerCalculator // https://en.wikipedia.org/wiki/Exponentiation_by_squaring - public static uint[] Pow(uint value, uint power) + public static void Pow(uint value, uint power, Span bits) { - // The basic pow method for a 32-bit integer. - // To spare memory allocations we first roughly - // estimate an upper bound for our buffers. + Pow(value != 0U ? stackalloc uint[1] { value } : default, power, bits); + } - int size = PowBound(power, 1, 1); - BitsBuffer v = new BitsBuffer(size, value); - return PowCore(power, ref v); + public static void Pow(ReadOnlySpan value, uint power, Span bits) + { + Debug.Assert(bits.Length == PowBound(power, value.Length)); + + uint[]? tempFromPool = null; + Span temp = (bits.Length <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : tempFromPool = ArrayPool.Shared.Rent(bits.Length)).Slice(0, bits.Length); + temp.Clear(); + + uint[]? valueCopyFromPool = null; + Span valueCopy = (bits.Length <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : valueCopyFromPool = ArrayPool.Shared.Rent(bits.Length)).Slice(0, bits.Length); + value.CopyTo(valueCopy); + valueCopy.Slice(value.Length).Clear(); + + PowCore(valueCopy, value.Length, temp, power, bits).CopyTo(bits); + + if (tempFromPool != null) + ArrayPool.Shared.Return(tempFromPool); + if (valueCopyFromPool != null) + ArrayPool.Shared.Return(valueCopyFromPool); } - public static uint[] Pow(uint[] value, uint power) + private static Span PowCore(Span value, int valueLength, Span temp, uint power, Span result) { - Debug.Assert(value != null); + Debug.Assert(value.Length >= valueLength); + Debug.Assert(temp.Length == result.Length); + Debug.Assert(value.Length == temp.Length); + + result[0] = 1; + int bitsLength = 1; - // The basic pow method for a big integer. - // To spare memory allocations we first roughly - // estimate an upper bound for our buffers. + // The basic pow algorithm using square-and-multiply. + while (power != 0) + { + if ((power & 1) == 1) + bitsLength = MultiplySelf(ref result, bitsLength, value.Slice(0, valueLength), ref temp); + if (power != 1) + valueLength = SquareSelf(ref value, valueLength, ref temp); + power = power >> 1; + } - int size = PowBound(power, value.Length, 1); - BitsBuffer v = new BitsBuffer(size, value); - return PowCore(power, ref v); + return result; } - private static uint[] PowCore(uint power, ref BitsBuffer value) + private static int MultiplySelf(ref Span left, int leftLength, ReadOnlySpan right, ref Span temp) { - // Executes the basic pow algorithm. + Debug.Assert(leftLength <= left.Length); - int size = value.GetSize(); + int resultLength = leftLength + right.Length; - BitsBuffer temp = new BitsBuffer(size, 0); - BitsBuffer result = new BitsBuffer(size, 1); + if (leftLength >= right.Length) + { + Multiply(left.Slice(0, leftLength), right, temp.Slice(0, resultLength)); + } + else + { + Multiply(right, left.Slice(0, leftLength), temp.Slice(0, resultLength)); + } + + left.Clear(); + //switch buffers + Span t = left; + left = temp; + temp = t; + return ActualLength(left.Slice(0, resultLength)); + } - PowCore(power, ref value, ref result, ref temp); + private static int SquareSelf(ref Span value, int valueLength, ref Span temp) + { + Debug.Assert(valueLength <= value.Length); + Debug.Assert(temp.Length >= valueLength + valueLength); - return result.GetBits(); + int resultLength = valueLength + valueLength; + + Square(value.Slice(0, valueLength), temp.Slice(0, resultLength)); + + value.Clear(); + //switch buffers + Span t = value; + value = temp; + temp = t; + return ActualLength(value.Slice(0, resultLength)); } - private static int PowBound(uint power, int valueLength, - int resultLength) + public static int PowBound(uint power, int valueLength) { // The basic pow algorithm, but instead of squaring // and multiplying we just sum up the lengths. + int resultLength = 1; while (power != 0) { checked @@ -71,64 +126,41 @@ private static int PowBound(uint power, int valueLength, return resultLength; } - private static void PowCore(uint power, ref BitsBuffer value, - ref BitsBuffer result, ref BitsBuffer temp) - { - // The basic pow algorithm using square-and-multiply. - - while (power != 0) - { - if ((power & 1) == 1) - result.MultiplySelf(ref value, ref temp); - if (power != 1) - value.SquareSelf(ref temp); - power = power >> 1; - } - } - public static uint Pow(uint value, uint power, uint modulus) { // The 32-bit modulus pow method for a 32-bit integer // raised by a 32-bit integer... - return PowCore(power, modulus, value, 1); + return PowCore(value, power, modulus, 1); } - public static uint Pow(uint[] value, uint power, uint modulus) + public static uint Pow(ReadOnlySpan value, uint power, uint modulus) { - Debug.Assert(value != null); - // The 32-bit modulus pow method for a big integer // raised by a 32-bit integer... uint v = Remainder(value, modulus); - return PowCore(power, modulus, v, 1); + return PowCore(v, power, modulus, 1); } - public static uint Pow(uint value, uint[] power, uint modulus) + public static uint Pow(uint value, ReadOnlySpan power, uint modulus) { - Debug.Assert(power != null); - // The 32-bit modulus pow method for a 32-bit integer // raised by a big integer... - return PowCore(power, modulus, value, 1); + return PowCore(value, power, modulus, 1); } - public static uint Pow(uint[] value, uint[] power, uint modulus) + public static uint Pow(ReadOnlySpan value, ReadOnlySpan power, uint modulus) { - Debug.Assert(value != null); - Debug.Assert(power != null); - // The 32-bit modulus pow method for a big integer // raised by a big integer... uint v = Remainder(value, modulus); - return PowCore(power, modulus, v, 1); + return PowCore(v, power, modulus, 1); } - private static uint PowCore(uint[] power, uint modulus, - ulong value, ulong result) + private static uint PowCore(ulong value, ReadOnlySpan power, uint modulus, ulong result) { // The 32-bit modulus pow algorithm for all but // the last power limb using square-and-multiply. @@ -145,11 +177,10 @@ private static uint PowCore(uint[] power, uint modulus, } } - return PowCore(power[power.Length - 1], modulus, value, result); + return PowCore(value, power[power.Length - 1], modulus, result); } - private static uint PowCore(uint power, uint modulus, - ulong value, ulong result) + private static uint PowCore(ulong value, uint power, uint modulus, ulong result) { // The 32-bit modulus pow algorithm for the last or // the only power limb using square-and-multiply. @@ -166,116 +197,221 @@ private static uint PowCore(uint power, uint modulus, return (uint)(result % modulus); } - public static uint[] Pow(uint value, uint power, uint[] modulus) + public static void Pow(uint value, uint power, + ReadOnlySpan modulus, Span bits) { - Debug.Assert(modulus != null); - - // The big modulus pow method for a 32-bit integer - // raised by a 32-bit integer... - - int size = modulus.Length + modulus.Length; - BitsBuffer v = new BitsBuffer(size, value); - return PowCore(power, modulus, ref v); + Pow(value != 0U ? stackalloc uint[1] { value } : default, power, modulus, bits); } - public static uint[] Pow(uint[] value, uint power, uint[] modulus) + public static void Pow(ReadOnlySpan value, uint power, + ReadOnlySpan modulus, Span bits) { - Debug.Assert(value != null); - Debug.Assert(modulus != null); + Debug.Assert(!modulus.IsEmpty); + Debug.Assert(bits.Length == modulus.Length + modulus.Length); // The big modulus pow method for a big integer // raised by a 32-bit integer... + uint[]? valueCopyFromPool = null; + int size = Math.Max(value.Length, bits.Length); + Span valueCopy = (size <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : valueCopyFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); + valueCopy.Clear(); + if (value.Length > modulus.Length) - value = Remainder(value, modulus); + { + Remainder(value, modulus, valueCopy); + } + else + { + value.CopyTo(valueCopy); + } - int size = modulus.Length + modulus.Length; - BitsBuffer v = new BitsBuffer(size, value); - return PowCore(power, modulus, ref v); - } + uint[]? tempFromPool = null; + Span temp = (bits.Length <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : tempFromPool = ArrayPool.Shared.Rent(bits.Length)).Slice(0, bits.Length); + temp.Clear(); - public static uint[] Pow(uint value, uint[] power, uint[] modulus) - { - Debug.Assert(power != null); - Debug.Assert(modulus != null); + PowCore(valueCopy, ActualLength(valueCopy), power, modulus, temp, bits); - // The big modulus pow method for a 32-bit integer - // raised by a big integer... + if (valueCopyFromPool != null) + ArrayPool.Shared.Return(valueCopyFromPool); + if (tempFromPool != null) + ArrayPool.Shared.Return(tempFromPool); + } - int size = modulus.Length + modulus.Length; - BitsBuffer v = new BitsBuffer(size, value); - return PowCore(power, modulus, ref v); + public static void Pow(uint value, ReadOnlySpan power, + ReadOnlySpan modulus, Span bits) + { + Pow(value != 0U ? stackalloc uint[1] { value } : default, power, modulus, bits); } - public static uint[] Pow(uint[] value, uint[] power, uint[] modulus) + public static void Pow(ReadOnlySpan value, ReadOnlySpan power, + ReadOnlySpan modulus, Span bits) { - Debug.Assert(value != null); - Debug.Assert(power != null); - Debug.Assert(modulus != null); + Debug.Assert(!modulus.IsEmpty); + Debug.Assert(bits.Length == modulus.Length + modulus.Length); // The big modulus pow method for a big integer // raised by a big integer... + int size = Math.Max(value.Length, bits.Length); + uint[]? valueCopyFromPool = null; + Span valueCopy = (size <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : valueCopyFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); + valueCopy.Clear(); + if (value.Length > modulus.Length) - value = Remainder(value, modulus); + { + Remainder(value, modulus, valueCopy); + } + else + { + value.CopyTo(valueCopy); + } + + uint[]? tempFromPool = null; + Span temp = (bits.Length <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : tempFromPool = ArrayPool.Shared.Rent(bits.Length)).Slice(0, bits.Length); + temp.Clear(); + + PowCore(valueCopy, ActualLength(valueCopy), power, modulus, temp, bits); - int size = modulus.Length + modulus.Length; - BitsBuffer v = new BitsBuffer(size, value); - return PowCore(power, modulus, ref v); + if (valueCopyFromPool != null) + ArrayPool.Shared.Return(valueCopyFromPool); + if (tempFromPool != null) + ArrayPool.Shared.Return(tempFromPool); } +#if DEBUG // Mutable for unit testing... - private static int ReducerThreshold = 32; - - private static uint[] PowCore(uint[] power, uint[] modulus, - ref BitsBuffer value) + private static +#else + private const +#endif + int ReducerThreshold = 32; + + private static void PowCore(Span value, int valueLength, + ReadOnlySpan power, ReadOnlySpan modulus, + Span temp, Span bits) { // Executes the big pow algorithm. - int size = value.GetSize(); - - BitsBuffer temp = new BitsBuffer(size, 0); - BitsBuffer result = new BitsBuffer(size, 1); + bits[0] = 1; if (modulus.Length < ReducerThreshold) { - PowCore(power, modulus, ref value, ref result, ref temp); + PowCore(value, valueLength, power, modulus, bits, 1, temp).CopyTo(bits); } else { - FastReducer reducer = new FastReducer(modulus); - PowCore(power, ref reducer, ref value, ref result, ref temp); + int size = modulus.Length * 2 + 1; + uint[]? rFromPool = null; + Span r = ((uint)size <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : rFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); + r.Clear(); + + size = r.Length - modulus.Length + 1; + uint[]? muFromPool = null; + Span mu = ((uint)size <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : muFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); + mu.Clear(); + + size = modulus.Length * 2 + 2; + uint[]? q1FromPool = null; + Span q1 = ((uint)size <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : q1FromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); + q1.Clear(); + + uint[]? q2FromPool = null; + Span q2 = ((uint)size <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : q2FromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); + q2.Clear(); + + FastReducer reducer = new FastReducer(modulus, r, mu, q1, q2); + + if (rFromPool != null) + ArrayPool.Shared.Return(rFromPool); + + PowCore(value, valueLength, power, reducer, bits, 1, temp).CopyTo(bits); + + if (muFromPool != null) + ArrayPool.Shared.Return(muFromPool); + if (q1FromPool != null) + ArrayPool.Shared.Return(q1FromPool); + if (q2FromPool != null) + ArrayPool.Shared.Return(q2FromPool); } - - return result.GetBits(); } - private static uint[] PowCore(uint power, uint[] modulus, - ref BitsBuffer value) + private static void PowCore(Span value, int valueLength, + uint power, ReadOnlySpan modulus, + Span temp, Span bits) { // Executes the big pow algorithm. - - int size = value.GetSize(); - - BitsBuffer temp = new BitsBuffer(size, 0); - BitsBuffer result = new BitsBuffer(size, 1); + bits[0] = 1; if (modulus.Length < ReducerThreshold) { - PowCore(power, modulus, ref value, ref result, ref temp); + PowCore(value, valueLength, power, modulus, bits, 1, temp).CopyTo(bits); } else { - FastReducer reducer = new FastReducer(modulus); - PowCore(power, ref reducer, ref value, ref result, ref temp); + int size = modulus.Length * 2 + 1; + uint[]? rFromPool = null; + Span r = ((uint)size <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : rFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); + r.Clear(); + + size = r.Length - modulus.Length + 1; + uint[]? muFromPool = null; + Span mu = ((uint)size <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : muFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); + mu.Clear(); + + size = modulus.Length * 2 + 2; + uint[]? q1FromPool = null; + Span q1 = ((uint)size <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : q1FromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); + q1.Clear(); + + uint[]? q2FromPool = null; + Span q2 = ((uint)size <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : q2FromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); + q2.Clear(); + + FastReducer reducer = new FastReducer(modulus, r, mu, q1, q2); + + if (rFromPool != null) + ArrayPool.Shared.Return(rFromPool); + + PowCore(value, valueLength, power, reducer, bits, 1, temp).CopyTo(bits); + + if (muFromPool != null) + ArrayPool.Shared.Return(muFromPool); + if (q1FromPool != null) + ArrayPool.Shared.Return(q1FromPool); + if (q2FromPool != null) + ArrayPool.Shared.Return(q2FromPool); } - - return result.GetBits(); } - private static void PowCore(uint[] power, uint[] modulus, - ref BitsBuffer value, ref BitsBuffer result, - ref BitsBuffer temp) + private static Span PowCore(Span value, int valueLength, + ReadOnlySpan power, ReadOnlySpan modulus, + Span result, int resultLength, + Span temp) { // The big modulus pow algorithm for all but // the last power limb using square-and-multiply. @@ -290,22 +426,22 @@ private static void PowCore(uint[] power, uint[] modulus, { if ((p & 1) == 1) { - result.MultiplySelf(ref value, ref temp); - result.Reduce(modulus); + resultLength = MultiplySelf(ref result, resultLength, value.Slice(0, valueLength), ref temp); + resultLength = Reduce(result.Slice(0, resultLength), modulus); } - value.SquareSelf(ref temp); - value.Reduce(modulus); + valueLength = SquareSelf(ref value, valueLength, ref temp); + valueLength = Reduce(value.Slice(0, valueLength), modulus); p = p >> 1; } } - PowCore(power[power.Length - 1], modulus, ref value, ref result, - ref temp); + return PowCore(value, valueLength, power[power.Length - 1], modulus, result, resultLength, temp); } - private static void PowCore(uint power, uint[] modulus, - ref BitsBuffer value, ref BitsBuffer result, - ref BitsBuffer temp) + private static Span PowCore(Span value, int valueLength, + uint power, ReadOnlySpan modulus, + Span result, int resultLength, + Span temp) { // The big modulus pow algorithm for the last or // the only power limb using square-and-multiply. @@ -317,21 +453,24 @@ private static void PowCore(uint power, uint[] modulus, { if ((power & 1) == 1) { - result.MultiplySelf(ref value, ref temp); - result.Reduce(modulus); + resultLength = MultiplySelf(ref result, resultLength, value.Slice(0, valueLength), ref temp); + resultLength = Reduce(result.Slice(0, resultLength), modulus); } if (power != 1) { - value.SquareSelf(ref temp); - value.Reduce(modulus); + valueLength = SquareSelf(ref value, valueLength, ref temp); + valueLength = Reduce(value.Slice(0, valueLength), modulus); } power = power >> 1; } + + return result.Slice(0, resultLength); } - private static void PowCore(uint[] power, ref FastReducer reducer, - ref BitsBuffer value, ref BitsBuffer result, - ref BitsBuffer temp) + private static Span PowCore(Span value, int valueLength, + ReadOnlySpan power, in FastReducer reducer, + Span result, int resultLength, + Span temp) { // The big modulus pow algorithm for all but // the last power limb using square-and-multiply. @@ -346,22 +485,22 @@ private static void PowCore(uint[] power, ref FastReducer reducer, { if ((p & 1) == 1) { - result.MultiplySelf(ref value, ref temp); - result.Reduce(ref reducer); + resultLength = MultiplySelf(ref result, resultLength, value.Slice(0, valueLength), ref temp); + resultLength = reducer.Reduce(result.Slice(0, resultLength)); } - value.SquareSelf(ref temp); - value.Reduce(ref reducer); + valueLength = SquareSelf(ref value, valueLength, ref temp); + valueLength = reducer.Reduce(value.Slice(0, valueLength)); p = p >> 1; } } - PowCore(power[power.Length - 1], ref reducer, ref value, ref result, - ref temp); + return PowCore(value, valueLength, power[power.Length - 1], reducer, result, resultLength, temp); } - private static void PowCore(uint power, ref FastReducer reducer, - ref BitsBuffer value, ref BitsBuffer result, - ref BitsBuffer temp) + private static Span PowCore(Span value, int valueLength, + uint power, in FastReducer reducer, + Span result, int resultLength, + Span temp) { // The big modulus pow algorithm for the last or // the only power limb using square-and-multiply. @@ -373,34 +512,18 @@ private static void PowCore(uint power, ref FastReducer reducer, { if ((power & 1) == 1) { - result.MultiplySelf(ref value, ref temp); - result.Reduce(ref reducer); + resultLength = MultiplySelf(ref result, resultLength, value.Slice(0, valueLength), ref temp); + resultLength = reducer.Reduce(result.Slice(0, resultLength)); } if (power != 1) { - value.SquareSelf(ref temp); - value.Reduce(ref reducer); + valueLength = SquareSelf(ref value, valueLength, ref temp); + valueLength = reducer.Reduce(value.Slice(0, valueLength)); } power = power >> 1; } - } - - private static int ActualLength(uint[] value) - { - // Since we're reusing memory here, the actual length - // of a given value may be less then the array's length - - return ActualLength(value, value.Length); - } - - private static int ActualLength(uint[] value, int length) - { - Debug.Assert(value != null); - Debug.Assert(length <= value.Length); - while (length > 0 && value[length - 1] == 0) - --length; - return length; + return result; } } } diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs index f77d02f37ccb3..4aeae650e76e6 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs @@ -1,40 +1,26 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Buffers; using System.Diagnostics; -using System.Security; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace System.Numerics { internal static partial class BigIntegerCalculator { - public static unsafe uint[] Square(uint[] value) - { - Debug.Assert(value != null); - - // Switching to unsafe pointers helps sparing - // some nasty index calculations... - - uint[] bits = new uint[value.Length + value.Length]; - - fixed (uint* v = value, b = bits) - { - Square(v, value.Length, - b, bits.Length); - } - - return bits; - } - +#if DEBUG // Mutable for unit testing... - private static int SquareThreshold = 32; - private static int AllocationThreshold = 256; + private static +#else + private const +#endif + int SquareThreshold = 32; - private static unsafe void Square(uint* value, int valueLength, - uint* bits, int bitsLength) + public static void Square(ReadOnlySpan value, Span bits) { - Debug.Assert(valueLength >= 0); - Debug.Assert(bitsLength == valueLength + valueLength); + Debug.Assert(bits.Length == value.Length + value.Length); // Executes different algorithms for computing z = a * a // based on the actual length of a. If a is "small" enough @@ -45,8 +31,12 @@ private static unsafe void Square(uint* value, int valueLength, // NOTE: useful thresholds needs some "empirical" testing, // which are smaller in DEBUG mode for testing purpose. - if (valueLength < SquareThreshold) + if (value.Length < SquareThreshold) { + // Switching to managed references helps eliminating + // index bounds check... + ref uint resultPtr = ref MemoryMarshal.GetReference(bits); + // Squares the bits using the "grammar-school" method. // Envisioning the "rhombus" of a pen-and-paper calculation // we see that computing z_i+j += a_j * a_i can be optimized @@ -58,20 +48,20 @@ private static unsafe void Square(uint* value, int valueLength, // = 2^64 - 1 (which perfectly matches with ulong!). But // here we would need an UInt65... Hence, we split these // operation and do some extra shifts. - - for (int i = 0; i < valueLength; i++) + for (int i = 0; i < value.Length; i++) { ulong carry = 0UL; + uint v = value[i]; for (int j = 0; j < i; j++) { - ulong digit1 = bits[i + j] + carry; - ulong digit2 = (ulong)value[j] * value[i]; - bits[i + j] = unchecked((uint)(digit1 + (digit2 << 1))); + ulong digit1 = Unsafe.Add(ref resultPtr, i + j) + carry; + ulong digit2 = (ulong)value[j] * v; + Unsafe.Add(ref resultPtr, i + j) = unchecked((uint)(digit1 + (digit2 << 1))); carry = (digit2 + (digit1 >> 1)) >> 31; } - ulong digits = (ulong)value[i] * value[i] + carry; - bits[i + i] = unchecked((uint)digits); - bits[i + i + 1] = (uint)(digits >> 32); + ulong digits = (ulong)v * v + carry; + Unsafe.Add(ref resultPtr, i + i) = unchecked((uint)digits); + Unsafe.Add(ref resultPtr, i + i + 1) = (uint)(digits >> 32); } } else @@ -88,81 +78,59 @@ private static unsafe void Square(uint* value, int valueLength, // Say we want to compute z = a * a ... // ... we need to determine our new length (just the half) - int n = valueLength >> 1; + int n = value.Length >> 1; int n2 = n << 1; // ... split value like a = (a_1 << n) + a_0 - uint* valueLow = value; - int valueLowLength = n; - uint* valueHigh = value + n; - int valueHighLength = valueLength - n; + ReadOnlySpan valueLow = value.Slice(0, n); + ReadOnlySpan valueHigh = value.Slice(n); // ... prepare our result array (to reuse its memory) - uint* bitsLow = bits; - int bitsLowLength = n2; - uint* bitsHigh = bits + n2; - int bitsHighLength = bitsLength - n2; + Span bitsLow = bits.Slice(0, n2); + Span bitsHigh = bits.Slice(n2); // ... compute z_0 = a_0 * a_0 (squaring again!) - Square(valueLow, valueLowLength, - bitsLow, bitsLowLength); + Square(valueLow, bitsLow); // ... compute z_2 = a_1 * a_1 (squaring again!) - Square(valueHigh, valueHighLength, - bitsHigh, bitsHighLength); + Square(valueHigh, bitsHigh); + + int foldLength = valueHigh.Length + 1; + uint[]? foldFromPool = null; + Span fold = ((uint)foldLength <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : foldFromPool = ArrayPool.Shared.Rent(foldLength)).Slice(0, foldLength); + fold.Clear(); - int foldLength = valueHighLength + 1; int coreLength = foldLength + foldLength; + uint[]? coreFromPool = null; + Span core = ((uint)coreLength <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : coreFromPool = ArrayPool.Shared.Rent(coreLength)).Slice(0, coreLength); + core.Clear(); - if (coreLength < AllocationThreshold) - { - uint* fold = stackalloc uint[foldLength]; - new Span(fold, foldLength).Clear(); - uint* core = stackalloc uint[coreLength]; - new Span(core, coreLength).Clear(); - - // ... compute z_a = a_1 + a_0 (call it fold...) - Add(valueHigh, valueHighLength, - valueLow, valueLowLength, - fold, foldLength); - - // ... compute z_1 = z_a * z_a - z_0 - z_2 - Square(fold, foldLength, - core, coreLength); - SubtractCore(bitsHigh, bitsHighLength, - bitsLow, bitsLowLength, - core, coreLength); - - // ... and finally merge the result! :-) - AddSelf(bits + n, bitsLength - n, core, coreLength); - } - else - { - fixed (uint* fold = new uint[foldLength], - core = new uint[coreLength]) - { - // ... compute z_a = a_1 + a_0 (call it fold...) - Add(valueHigh, valueHighLength, - valueLow, valueLowLength, - fold, foldLength); - - // ... compute z_1 = z_a * z_a - z_0 - z_2 - Square(fold, foldLength, - core, coreLength); - SubtractCore(bitsHigh, bitsHighLength, - bitsLow, bitsLowLength, - core, coreLength); - - // ... and finally merge the result! :-) - AddSelf(bits + n, bitsLength - n, core, coreLength); - } - } + // ... compute z_a = a_1 + a_0 (call it fold...) + Add(valueHigh, valueLow, fold); + + // ... compute z_1 = z_a * z_a - z_0 - z_2 + Square(fold, core); + + if (foldFromPool != null) + ArrayPool.Shared.Return(foldFromPool); + + SubtractCore(bitsHigh, bitsLow, core); + + // ... and finally merge the result! :-) + AddSelf(bits.Slice(n), core); + + if (coreFromPool != null) + ArrayPool.Shared.Return(coreFromPool); } } - public static uint[] Multiply(uint[] left, uint right) + public static void Multiply(ReadOnlySpan left, uint right, Span bits) { - Debug.Assert(left != null); + Debug.Assert(bits.Length == left.Length + 1); // Executes the multiplication for one big and one 32-bit integer. // Since every step holds the already slightly familiar equation @@ -171,51 +139,28 @@ public static uint[] Multiply(uint[] left, uint right) int i = 0; ulong carry = 0UL; - uint[] bits = new uint[left.Length + 1]; - for (; i < left.Length; i++) + for ( ; i < left.Length; i++) { ulong digits = (ulong)left[i] * right + carry; bits[i] = unchecked((uint)digits); carry = digits >> 32; } bits[i] = (uint)carry; - - return bits; - } - - public static unsafe uint[] Multiply(uint[] left, uint[] right) - { - Debug.Assert(left != null); - Debug.Assert(right != null); - Debug.Assert(left.Length >= right.Length); - - // Switching to unsafe pointers helps sparing - // some nasty index calculations... - - uint[] bits = new uint[left.Length + right.Length]; - - fixed (uint* l = left, r = right, b = bits) - { - Multiply(l, left.Length, - r, right.Length, - b, bits.Length); - } - - return bits; } +#if DEBUG // Mutable for unit testing... - private static int MultiplyThreshold = 32; + private static +#else + private const +#endif + int MultiplyThreshold = 32; - private static unsafe void Multiply(uint* left, int leftLength, - uint* right, int rightLength, - uint* bits, int bitsLength) + public static void Multiply(ReadOnlySpan left, ReadOnlySpan right, Span bits) { - Debug.Assert(leftLength >= 0); - Debug.Assert(rightLength >= 0); - Debug.Assert(leftLength >= rightLength); - Debug.Assert(bitsLength == leftLength + rightLength); + Debug.Assert(left.Length >= right.Length); + Debug.Assert(bits.Length == left.Length + right.Length); // Executes different algorithms for computing z = a * b // based on the actual length of b. If b is "small" enough @@ -226,8 +171,12 @@ private static unsafe void Multiply(uint* left, int leftLength, // NOTE: useful thresholds needs some "empirical" testing, // which are smaller in DEBUG mode for testing purpose. - if (rightLength < MultiplyThreshold) + if (right.Length < MultiplyThreshold) { + // Switching to managed references helps eliminating + // index bounds check... + ref uint resultPtr = ref MemoryMarshal.GetReference(bits); + // Multiplies the bits using the "grammar-school" method. // Envisioning the "rhombus" of a pen-and-paper calculation // should help getting the idea of these two loops... @@ -235,17 +184,17 @@ private static unsafe void Multiply(uint* left, int leftLength, // z_i+j + a_j * b_i + c <= 2(2^32 - 1) + (2^32 - 1)^2 = // = 2^64 - 1 (which perfectly matches with ulong!). - for (int i = 0; i < rightLength; i++) + for (int i = 0; i < right.Length; i++) { ulong carry = 0UL; - for (int j = 0; j < leftLength; j++) + for (int j = 0; j < left.Length; j++) { - ulong digits = bits[i + j] + carry - + (ulong)left[j] * right[i]; - bits[i + j] = unchecked((uint)digits); + ref uint elementPtr = ref Unsafe.Add(ref resultPtr, i + j); + ulong digits = elementPtr + carry + (ulong)left[j] * right[i]; + elementPtr = unchecked((uint)digits); carry = digits >> 32; } - bits[i + leftLength] = (uint)carry; + Unsafe.Add(ref resultPtr, i + left.Length) = (uint)carry; } } else @@ -262,111 +211,77 @@ private static unsafe void Multiply(uint* left, int leftLength, // Say we want to compute z = a * b ... // ... we need to determine our new length (just the half) - int n = rightLength >> 1; + int n = right.Length >> 1; int n2 = n << 1; // ... split left like a = (a_1 << n) + a_0 - uint* leftLow = left; - int leftLowLength = n; - uint* leftHigh = left + n; - int leftHighLength = leftLength - n; + ReadOnlySpan leftLow = left.Slice(0, n); + ReadOnlySpan leftHigh = left.Slice(n); // ... split right like b = (b_1 << n) + b_0 - uint* rightLow = right; - int rightLowLength = n; - uint* rightHigh = right + n; - int rightHighLength = rightLength - n; + ReadOnlySpan rightLow = right.Slice(0, n); + ReadOnlySpan rightHigh = right.Slice(n); // ... prepare our result array (to reuse its memory) - uint* bitsLow = bits; - int bitsLowLength = n2; - uint* bitsHigh = bits + n2; - int bitsHighLength = bitsLength - n2; + Span bitsLow = bits.Slice(0, n2); + Span bitsHigh = bits.Slice(n2); // ... compute z_0 = a_0 * b_0 (multiply again) - Multiply(leftLow, leftLowLength, - rightLow, rightLowLength, - bitsLow, bitsLowLength); + Multiply(leftLow, rightLow, bitsLow); // ... compute z_2 = a_1 * b_1 (multiply again) - Multiply(leftHigh, leftHighLength, - rightHigh, rightHighLength, - bitsHigh, bitsHighLength); + Multiply(leftHigh, rightHigh, bitsHigh); + + int leftFoldLength = leftHigh.Length + 1; + uint[]? leftFoldFromPool = null; + Span leftFold = ((uint)leftFoldLength <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : leftFoldFromPool = ArrayPool.Shared.Rent(leftFoldLength)).Slice(0, leftFoldLength); + leftFold.Clear(); + + int rightFoldLength = rightHigh.Length + 1; + uint[]? rightFoldFromPool = null; + Span rightFold = ((uint)rightFoldLength <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : rightFoldFromPool = ArrayPool.Shared.Rent(rightFoldLength)).Slice(0, rightFoldLength); + rightFold.Clear(); - int leftFoldLength = leftHighLength + 1; - int rightFoldLength = rightHighLength + 1; int coreLength = leftFoldLength + rightFoldLength; + uint[]? coreFromPool = null; + Span core = ((uint)coreLength <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : coreFromPool = ArrayPool.Shared.Rent(coreLength)).Slice(0, coreLength); + core.Clear(); - if (coreLength < AllocationThreshold) - { - uint* leftFold = stackalloc uint[leftFoldLength]; - new Span(leftFold, leftFoldLength).Clear(); - uint* rightFold = stackalloc uint[rightFoldLength]; - new Span(rightFold, rightFoldLength).Clear(); - uint* core = stackalloc uint[coreLength]; - new Span(core, coreLength).Clear(); - - // ... compute z_a = a_1 + a_0 (call it fold...) - Add(leftHigh, leftHighLength, - leftLow, leftLowLength, - leftFold, leftFoldLength); - - // ... compute z_b = b_1 + b_0 (call it fold...) - Add(rightHigh, rightHighLength, - rightLow, rightLowLength, - rightFold, rightFoldLength); - - // ... compute z_1 = z_a * z_b - z_0 - z_2 - Multiply(leftFold, leftFoldLength, - rightFold, rightFoldLength, - core, coreLength); - SubtractCore(bitsHigh, bitsHighLength, - bitsLow, bitsLowLength, - core, coreLength); - - // ... and finally merge the result! :-) - AddSelf(bits + n, bitsLength - n, core, coreLength); - } - else - { - fixed (uint* leftFold = new uint[leftFoldLength], - rightFold = new uint[rightFoldLength], - core = new uint[coreLength]) - { - // ... compute z_a = a_1 + a_0 (call it fold...) - Add(leftHigh, leftHighLength, - leftLow, leftLowLength, - leftFold, leftFoldLength); - - // ... compute z_b = b_1 + b_0 (call it fold...) - Add(rightHigh, rightHighLength, - rightLow, rightLowLength, - rightFold, rightFoldLength); - - // ... compute z_1 = z_a * z_b - z_0 - z_2 - Multiply(leftFold, leftFoldLength, - rightFold, rightFoldLength, - core, coreLength); - SubtractCore(bitsHigh, bitsHighLength, - bitsLow, bitsLowLength, - core, coreLength); - - // ... and finally merge the result! :-) - AddSelf(bits + n, bitsLength - n, core, coreLength); - } - } + // ... compute z_a = a_1 + a_0 (call it fold...) + Add(leftHigh, leftLow, leftFold); + + // ... compute z_b = b_1 + b_0 (call it fold...) + Add(rightHigh, rightLow, rightFold); + + // ... compute z_1 = z_a * z_b - z_0 - z_2 + Multiply(leftFold, rightFold, core); + + if (leftFoldFromPool != null) + ArrayPool.Shared.Return(leftFoldFromPool); + + if (rightFoldFromPool != null) + ArrayPool.Shared.Return(rightFoldFromPool); + + SubtractCore(bitsHigh, bitsLow, core); + + // ... and finally merge the result! :-) + AddSelf(bits.Slice(n), core); + + if (coreFromPool != null) + ArrayPool.Shared.Return(coreFromPool); } } - private static unsafe void SubtractCore(uint* left, int leftLength, - uint* right, int rightLength, - uint* core, int coreLength) + private static void SubtractCore(ReadOnlySpan left, ReadOnlySpan right, Span core) { - Debug.Assert(leftLength >= 0); - Debug.Assert(rightLength >= 0); - Debug.Assert(coreLength >= 0); - Debug.Assert(leftLength >= rightLength); - Debug.Assert(coreLength >= leftLength); + Debug.Assert(left.Length >= right.Length); + Debug.Assert(core.Length >= left.Length); // Executes a special subtraction algorithm for the multiplication, // which needs to subtract two different values from a core value, @@ -378,19 +293,26 @@ private static unsafe void SubtractCore(uint* left, int leftLength, int i = 0; long carry = 0L; - for (; i < rightLength; i++) + // Switching to managed references helps eliminating + // index bounds check... + ref uint leftPtr = ref MemoryMarshal.GetReference(left); + ref uint corePtr = ref MemoryMarshal.GetReference(core); + + for ( ; i < right.Length; i++) { - long digit = (core[i] + carry) - left[i] - right[i]; - core[i] = unchecked((uint)digit); + long digit = (Unsafe.Add(ref corePtr, i) + carry) - Unsafe.Add(ref leftPtr, i) - right[i]; + Unsafe.Add(ref corePtr, i) = unchecked((uint)digit); carry = digit >> 32; } - for (; i < leftLength; i++) + + for ( ; i < left.Length; i++) { - long digit = (core[i] + carry) - left[i]; - core[i] = unchecked((uint)digit); + long digit = (Unsafe.Add(ref corePtr, i) + carry) - left[i]; + Unsafe.Add(ref corePtr, i) = unchecked((uint)digit); carry = digit >> 32; } - for (; carry != 0 && i < coreLength; i++) + + for ( ; carry != 0 && i < core.Length; i++) { long digit = core[i] + carry; core[i] = (uint)digit; diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs new file mode 100644 index 0000000000000..37d4ae7596568 --- /dev/null +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs @@ -0,0 +1,61 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Numerics +{ + internal static partial class BigIntegerCalculator + { +#if DEBUG + // Mutable for unit testing... + internal static +#else + internal const +#endif + int StackAllocThreshold = 64; + + public static int Compare(ReadOnlySpan left, ReadOnlySpan right) + { + if (left.Length < right.Length) + return -1; + if (left.Length > right.Length) + return 1; + + for (int i = left.Length - 1; i >= 0; i--) + { + uint leftElement = left[i]; + uint rightElement = right[i]; + if (leftElement < rightElement) + return -1; + if (leftElement > rightElement) + return 1; + } + + return 0; + } + + private static int ActualLength(ReadOnlySpan value) + { + // Since we're reusing memory here, the actual length + // of a given value may be less then the array's length + + int length = value.Length; + + while (length > 0 && value[length - 1] == 0) + --length; + return length; + } + + private static int Reduce(Span bits, ReadOnlySpan modulus) + { + // Executes a modulo operation using the divide operation. + + if (bits.Length >= modulus.Length) + { + Divide(bits, modulus, default); + + return ActualLength(bits.Slice(0, modulus.Length)); + } + return bits.Length; + } + } +} \ No newline at end of file diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs index 5ce7919e830bf..2d7395e2e83dd 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs @@ -410,11 +410,11 @@ private static bool HexNumberToBigInteger(ref BigNumberBuffer number, out BigInt bool isNegative = HexConverter.FromChar(number.digits[0]) >= 8; uint partialValue = (isNegative && partialDigitCount > 0) ? 0xFFFFFFFFu : 0; - int[]? arrayFromPool = null; + uint[]? arrayFromPool = null; - Span bitsBuffer = (blockCount <= BigInteger.StackallocUInt32Limit) - ? stackalloc uint[blockCount] - : MemoryMarshal.Cast((arrayFromPool = ArrayPool.Shared.Rent(blockCount)).AsSpan(0, blockCount)); + Span bitsBuffer = ((uint)blockCount <= BigIntegerCalculator.StackAllocThreshold + ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : arrayFromPool = ArrayPool.Shared.Rent(blockCount)).Slice(0, blockCount); int bitsBufferPos = blockCount - 1; @@ -489,14 +489,14 @@ private static bool HexNumberToBigInteger(ref BigNumberBuffer number, out BigInt { if (arrayFromPool != null) { - ArrayPool.Shared.Return(arrayFromPool); + ArrayPool.Shared.Return(arrayFromPool); } } } private static bool NumberToBigInteger(ref BigNumberBuffer number, out BigInteger result) { - Span stackBuffer = stackalloc uint[BigInteger.StackallocUInt32Limit]; + Span stackBuffer = stackalloc uint[BigIntegerCalculator.StackAllocThreshold]; Span currentBuffer = stackBuffer; int currentBufferSize = 0; int[]? arrayFromPool = null; diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/Complex.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/Complex.cs index 9325be0d806c2..d2f207229e313 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/Complex.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/Complex.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Globalization; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; @@ -851,4 +850,4 @@ public static explicit operator Complex(decimal value) return new Complex((double)value, 0.0); } } -} +} \ No newline at end of file diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/NumericsHelpers.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/NumericsHelpers.cs index e89bc4acd13e8..ff9d263edcb51 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/NumericsHelpers.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/NumericsHelpers.cs @@ -59,7 +59,7 @@ public static double GetDoubleFromParts(int sign, int exp, ulong man) else { // Normalize so that 0x0010 0000 0000 0000 is the highest bit set. - int cbitShift = CbitHighZero(man) - 11; + int cbitShift = BitOperations.LeadingZeroCount(man) - 11; if (cbitShift < 0) man >>= -cbitShift; else @@ -108,11 +108,12 @@ public static double GetDoubleFromParts(int sign, int exp, ulong man) // a mutation and needs to be used with care for immutable types. public static void DangerousMakeTwosComplement(Span d) { - if (d != null && d.Length > 0) + if (d.Length > 0) { d[0] = unchecked(~d[0] + 1); int i = 1; + // first do complement and +1 as long as carry is needed for (; d[i - 1] == 0 && i < d.Length; i++) { @@ -139,53 +140,5 @@ public static uint Abs(int a) return ((uint)a ^ mask) - mask; } } - - public static uint CombineHash(uint u1, uint u2) - { - return ((u1 << 7) | (u1 >> 25)) ^ u2; - } - - public static int CombineHash(int n1, int n2) - { - return unchecked((int)CombineHash((uint)n1, (uint)n2)); - } - - public static int CbitHighZero(uint u) - { - if (u == 0) - return 32; - - int cbit = 0; - if ((u & 0xFFFF0000) == 0) - { - cbit += 16; - u <<= 16; - } - if ((u & 0xFF000000) == 0) - { - cbit += 8; - u <<= 8; - } - if ((u & 0xF0000000) == 0) - { - cbit += 4; - u <<= 4; - } - if ((u & 0xC0000000) == 0) - { - cbit += 2; - u <<= 2; - } - if ((u & 0x80000000) == 0) - cbit += 1; - return cbit; - } - - public static int CbitHighZero(ulong uu) - { - if ((uu & 0xFFFFFFFF00000000) == 0) - return 32 + CbitHighZero((uint)uu); - return CbitHighZero((uint)(uu >> 32)); - } } } diff --git a/src/libraries/System.Runtime.Numerics/tests/BigInteger/BigIntTools.cs b/src/libraries/System.Runtime.Numerics/tests/BigInteger/BigIntTools.cs index 519b1d9e5b397..33d6614483717 100644 --- a/src/libraries/System.Runtime.Numerics/tests/BigInteger/BigIntTools.cs +++ b/src/libraries/System.Runtime.Numerics/tests/BigInteger/BigIntTools.cs @@ -59,6 +59,9 @@ public static void RunWithFakeThreshold(string name, int value, Action action) return; // Internal frame types are not reflectable on AoT platforms. Skip the test. FieldInfo field = internalCalculator.GetDeclaredField(name); + if (field is null || field.IsLiteral) + return; // in Release config the field may be const + int lastValue = (int)field.GetValue(null); field.SetValue(null, value); try diff --git a/src/libraries/System.Runtime.Numerics/tests/BigInteger/multiply.cs b/src/libraries/System.Runtime.Numerics/tests/BigInteger/multiply.cs index 72be36c581923..a2646d5bd6cb8 100644 --- a/src/libraries/System.Runtime.Numerics/tests/BigInteger/multiply.cs +++ b/src/libraries/System.Runtime.Numerics/tests/BigInteger/multiply.cs @@ -44,7 +44,7 @@ public static void RunMultiply_TwoLargeBigIntegers_Threshold() // Again, with lower threshold BigIntTools.Utils.RunWithFakeThreshold("SquareThreshold", 8, () => BigIntTools.Utils.RunWithFakeThreshold("MultiplyThreshold", 8, () => - BigIntTools.Utils.RunWithFakeThreshold("AllocationThreshold", 8, RunMultiply_TwoLargeBigIntegers) + BigIntTools.Utils.RunWithFakeThreshold("StackAllocThreshold", 8, RunMultiply_TwoLargeBigIntegers) ) ); } From 17a1e383bcaa10b39cd563e13baf37c109ed07ce Mon Sep 17 00:00:00 2001 From: Natalia Kondratyeva Date: Fri, 8 Oct 2021 19:57:36 +0200 Subject: [PATCH 015/106] Fix WebSocket cancel test for non-browser (#60173) Non-browser WS server used for ConnectAsync_Cancel_ThrowsCancellationException doesn't support "delay20sec", only "delay10sec". This basically reverts the change in this line from #58199 for non-browser platforms. Contributes to #58355 --- src/libraries/System.Net.WebSockets.Client/tests/CancelTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Net.WebSockets.Client/tests/CancelTest.cs b/src/libraries/System.Net.WebSockets.Client/tests/CancelTest.cs index 9e400c3f723a9..752ca8ece8189 100644 --- a/src/libraries/System.Net.WebSockets.Client/tests/CancelTest.cs +++ b/src/libraries/System.Net.WebSockets.Client/tests/CancelTest.cs @@ -22,7 +22,7 @@ public async Task ConnectAsync_Cancel_ThrowsCancellationException(Uri server) var cts = new CancellationTokenSource(100); var ub = new UriBuilder(server); - ub.Query = "delay20sec"; + ub.Query = PlatformDetection.IsBrowser ? "delay20sec" : "delay10sec"; var ex = await Assert.ThrowsAnyAsync(() => cws.ConnectAsync(ub.Uri, cts.Token)); Assert.True(WebSocketState.Closed == cws.State, $"Actual {cws.State} when {ex}"); From 61c204398c5386789cd887b65012ff83d97d8f19 Mon Sep 17 00:00:00 2001 From: Aaron Kunkle Date: Fri, 8 Oct 2021 11:46:36 -0700 Subject: [PATCH 016/106] Fix Robocopy Verification (#60196) * Fix robocopy exit code response * Fix failure condition * Rollback performance-setup.sh --- eng/testing/performance/performance-setup.ps1 | 5 +- eng/testing/performance/performance-setup.sh | 48 +++++-------------- 2 files changed, 15 insertions(+), 38 deletions(-) diff --git a/eng/testing/performance/performance-setup.ps1 b/eng/testing/performance/performance-setup.ps1 index b4db23932ce34..b2cf0d955c95f 100644 --- a/eng/testing/performance/performance-setup.ps1 +++ b/eng/testing/performance/performance-setup.ps1 @@ -67,10 +67,13 @@ function Verify-Robocopy { [string]$Source ) - if ($LASTEXITCODE -ne 0 -or !$?) { + if ($LASTEXITCODE -gt 8 -or !$?) { Write-Output "Failed to copy ${Source}: exit code $LASTEXITCODE" exit $LASTEXITCODE } + else if ($LASTEXITCODE -gt 1) { + Write-Output "Unusual result when copying ${Source}: exit code $LASTEXITCODE" + } } $RunFromPerformanceRepo = ($Repository -eq "dotnet/performance") -or ($Repository -eq "dotnet-performance") diff --git a/eng/testing/performance/performance-setup.sh b/eng/testing/performance/performance-setup.sh index c897b1019aae6..c99ad1f48f3fa 100755 --- a/eng/testing/performance/performance-setup.sh +++ b/eng/testing/performance/performance-setup.sh @@ -31,24 +31,6 @@ use_latest_dotnet=false logical_machine= javascript_engine="v8" -verified_mv() -{ - mv $1 $2 - if [ "$?" -ne "0" ]; then - echo "Failed to move $1 to $2" - exit 1 - fi -} - -verified_rsync() -{ - rsync -a --progress $1 $2 - if [ "$?" -ne "0" ]; then - echo "Failed to sync $1 to $2" - exit 1 - fi -} - while (($# > 0)); do lowerI="$(echo $1 | tr "[:upper:]" "[:lower:]")" case $lowerI in @@ -273,11 +255,7 @@ if [[ "$monoaot" == "true" ]]; then extra_benchmark_dotnet_arguments="$extra_benchmark_dotnet_arguments --category-exclusion-filter NoAOT" fi -cleaned_branch_name="main" -if [[ $branch == *"refs/heads/release"* ]]; then - cleaned_branch_name=${branch/refs\/heads\//} -fi -common_setup_arguments="--channel $cleaned_branch_name --queue $queue --build-number $build_number --build-configs $configurations --architecture $architecture" +common_setup_arguments="--channel main --queue $queue --build-number $build_number --build-configs $configurations --architecture $architecture" setup_arguments="--repository https://github.com/$repository --branch $branch --get-perf-hash --commit-sha $commit_sha $common_setup_arguments" if [[ "$run_from_perf_repo" = true ]]; then @@ -289,28 +267,24 @@ else git clone --branch main --depth 1 --quiet https://github.com/dotnet/performance.git $performance_directory # uncomment to use BenchmarkDotNet sources instead of nuget packages # git clone https://github.com/dotnet/BenchmarkDotNet.git $benchmark_directory - if [ "$?" -ne "0" ]; then - echo "git clone failed with code $?" - exit 1 - fi - + docs_directory=$performance_directory/docs - verified_mv $docs_directory $workitem_directory + mv $docs_directory $workitem_directory fi if [[ "$wasm_runtime_loc" != "" ]]; then using_wasm=true wasm_dotnet_path=$payload_directory/dotnet-wasm - verified_mv $wasm_runtime_loc $wasm_dotnet_path + mv $wasm_runtime_loc $wasm_dotnet_path # install emsdk, $source_directory/src/mono/wasm/ has the nuget.config with require feed. EMSDK may be available in the payload in a different directory, should visit this install to avoid deplicated payload. pushd $source_directory/src/mono/wasm/ make provision-wasm EMSDK_PATH = $source_directory/src/mono/wasm/emsdk popd # wasm aot and interpreter need some source code from dotnet\runtime repo - verified_rsync -aq --progress $source_directory/* $wasm_dotnet_path --exclude Payload --exclude docs --exclude src/coreclr --exclude src/tests --exclude artifacts/obj --exclude artifacts/log --exclude artifacts/tests --exclude __download__ + rsync -aq --progress $source_directory/* $wasm_dotnet_path --exclude Payload --exclude docs --exclude src/coreclr --exclude src/tests --exclude artifacts/obj --exclude artifacts/log --exclude artifacts/tests --exclude __download__ # copy wasm build drop to the location that aot and interpreter build expects - verified_rsync -a --progress $wasm_dotnet_path/artifacts/BrowserWasm/artifacts/* $wasm_dotnet_path/artifacts + rsync -a --progress $wasm_dotnet_path/artifacts/BrowserWasm/artifacts/* $wasm_dotnet_path/artifacts rm -r $wasm_dotnet_path/artifacts/BrowserWasm/artifacts if [[ "$wasmaot" == "true" ]]; then extra_benchmark_dotnet_arguments="$extra_benchmark_dotnet_arguments --wasmEngine /home/helixbot/.jsvu/$javascript_engine --runtimeSrcDir \$HELIX_CORRELATION_PAYLOAD/dotnet-wasm --aotcompilermode wasm --buildTimeout 3600" @@ -322,23 +296,23 @@ fi if [[ "$mono_dotnet" != "" ]] && [[ "$monoaot" == "false" ]]; then using_mono=true mono_dotnet_path=$payload_directory/dotnet-mono - verified_mv $mono_dotnet $mono_dotnet_path + mv $mono_dotnet $mono_dotnet_path fi if [[ "$monoaot" == "true" ]]; then monoaot_dotnet_path=$payload_directory/monoaot - verified_mv $monoaot_path $monoaot_dotnet_path + mv $monoaot_path $monoaot_dotnet_path extra_benchmark_dotnet_arguments="$extra_benchmark_dotnet_arguments --runtimes monoaotllvm --aotcompilerpath \$HELIX_CORRELATION_PAYLOAD/monoaot/sgen/mini/mono-sgen --customruntimepack \$HELIX_CORRELATION_PAYLOAD/monoaot/pack --aotcompilermode llvm" fi if [[ "$use_core_run" = true ]]; then new_core_root=$payload_directory/Core_Root - verified_mv $core_root_directory $new_core_root + mv $core_root_directory $new_core_root fi if [[ "$use_baseline_core_run" = true ]]; then - new_baseline_core_root=$payload_directory/Baseline_Core_Root - verified_mv $baseline_core_root_directory $new_baseline_core_root + new_baseline_core_root=$payload_directory/Baseline_Core_Root + mv $baseline_core_root_directory $new_baseline_core_root fi ci=true From ce4eb48eb8950e3a1454149af14318494a1ca95e Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Fri, 8 Oct 2021 23:08:07 +0200 Subject: [PATCH 017/106] [mono][wasm] Add beginnings of support for WASI. (#59752) * [mono][wasm] Add beginnings of support for WASI. * Use mono_process_current_pid () instead of getpid (). * Remove MONO_STRICT_IO_EMULATION option. * Fix the build when eventpipe is disabled. * Use HOST_BROWSER instead of HOST_WASM to protect emscripten only code. --- src/mono/CMakeLists.txt | 15 ++- src/mono/Makefile | 2 +- src/mono/cmake/config.h.in | 21 ++++ src/mono/cmake/configure.cmake | 21 +++- src/mono/mono/component/debugger-agent.c | 3 +- src/mono/mono/component/debugger-stub.c | 6 +- src/mono/mono/eventpipe/CMakeLists.txt | 4 +- src/mono/mono/eventpipe/gen-eventing.cmake | 4 +- src/mono/mono/metadata/assembly.c | 3 +- src/mono/mono/metadata/object.c | 2 +- src/mono/mono/metadata/w32file-unix.c | 115 --------------------- src/mono/mono/mini/mini-wasm.c | 19 ++-- src/mono/mono/sgen/sgen-gc.c | 6 +- src/mono/mono/utils/mono-mmap-wasm.c | 2 +- src/mono/mono/utils/mono-proclib.c | 4 +- src/mono/mono/utils/mono-signal-handler.h | 2 + src/mono/mono/utils/mono-threads-wasm.c | 35 ++++++- 17 files changed, 111 insertions(+), 153 deletions(-) diff --git a/src/mono/CMakeLists.txt b/src/mono/CMakeLists.txt index 51c5dda0763d6..823926370dd4a 100644 --- a/src/mono/CMakeLists.txt +++ b/src/mono/CMakeLists.txt @@ -234,6 +234,15 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Emscripten") # sys/random.h exists, but its not found set(HAVE_SYS_RANDOM_H 1) set(INTERNAL_ZLIB 1) +elseif(CMAKE_SYSTEM_NAME STREQUAL "WASI") + set(HOST_WASI 1) + add_definitions(-D_WASI_EMULATED_SIGNAL -D_WASI_EMULATED_MMAN) + add_definitions(-DNO_GLOBALIZATION_SHIM) + add_definitions(-D_THREAD_SAFE) + set(DISABLE_SHARED_LIBS 1) + set(INTERNAL_ZLIB 1) + set(DISABLE_EXECUTABLES 1) + set(DISABLE_COMPONENTS 1) elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows") set(HOST_WIN32 1) set(EXE_SUFFIX ".exe") @@ -298,6 +307,8 @@ elseif(TARGET_SYSTEM_NAME STREQUAL "Emscripten") if (CMAKE_BUILD_TYPE STREQUAL "Release") add_compile_options(-Os) endif() +elseif(TARGET_SYSTEM_NAME STREQUAL "WASI") + set(TARGET_WASI 1) elseif(TARGET_SYSTEM_NAME STREQUAL "Windows") set(TARGET_WIN32 1) elseif(TARGET_SYSTEM_NAME STREQUAL "SunOS") @@ -350,7 +361,7 @@ elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "arm") set(NO_UNALIGNED_ACCESS 1) elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "s390x") set(HOST_S390X 1) -elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "wasm") +elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "wasm" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "wasm32") set(HOST_WASM 1) else() message(FATAL_ERROR "CMAKE_SYSTEM_PROCESSOR='${CMAKE_SYSTEM_PROCESSOR}' not supported.") @@ -407,7 +418,7 @@ elseif(TARGET_ARCH STREQUAL "s390x") set(MONO_ARCHITECTURE "\"s390x\"") set(TARGET_SIZEOF_VOID_P 8) set(SIZEOF_REGISTER 8) -elseif(TARGET_ARCH STREQUAL "wasm") +elseif(TARGET_ARCH STREQUAL "wasm" OR TARGET_ARCH STREQUAL "wasm32") set(TARGET_WASM 1) set(MONO_ARCHITECTURE "\"wasm\"") set(TARGET_SIZEOF_VOID_P 4) diff --git a/src/mono/Makefile b/src/mono/Makefile index d2cce8e8bda73..e4c6727b00ad4 100644 --- a/src/mono/Makefile +++ b/src/mono/Makefile @@ -30,7 +30,7 @@ run-sample-coreclr: # build System.Private.CoreLib.dll bcl corelib: - ../.././build.sh -c $(MONO_RUNTIME_CONFIG) -subset Mono.CoreLib + ../.././build.sh -c $(MONO_RUNTIME_CONFIG) -subset Mono.CoreLib+Libs.Pretest # build runtime and copy to artifacts runtime: diff --git a/src/mono/cmake/config.h.in b/src/mono/cmake/config.h.in index 96aa8939ade7c..136a22bbe1dd5 100644 --- a/src/mono/cmake/config.h.in +++ b/src/mono/cmake/config.h.in @@ -134,6 +134,15 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_UNISTD_H 1 +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SIGNAL_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SETJMP_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYSLOG_H 1 + /* Define to 1 if `major', `minor', and `makedev' are declared in . */ #cmakedefine MAJOR_IN_MKDEV 1 @@ -623,6 +632,12 @@ /* Have access */ #cmakedefine HAVE_ACCESS 1 +/* Have getpid */ +#cmakedefine HAVE_GETPID 1 + +/* Have mktemp */ +#cmakedefine HAVE_MKTEMP 1 + /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_ERRNO_H 1 @@ -839,6 +854,12 @@ /* ... */ #cmakedefine HOST_WASM 1 +/* ... */ +#cmakedefine HOST_BROWSER 1 + +/* ... */ +#cmakedefine HOST_WASI 1 + /* ... */ #cmakedefine HOST_X86 1 diff --git a/src/mono/cmake/configure.cmake b/src/mono/cmake/configure.cmake index 438e71783cc81..5bad48a719b85 100644 --- a/src/mono/cmake/configure.cmake +++ b/src/mono/cmake/configure.cmake @@ -20,6 +20,10 @@ if(HOST_SOLARIS) set(CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS} -DGC_SOLARIS_THREADS -DGC_SOLARIS_PTHREADS -D_REENTRANT -D_POSIX_PTHREAD_SEMANTICS -DUSE_MMAP -DUSE_MUNMAP -DHOST_SOLARIS -D__EXTENSIONS__ -D_XPG4_2") endif() +if(HOST_WASI) + set(CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS} -D_WASI_EMULATED_SIGNAL -D_WASI_EMULATED_MMAN") +endif() + function(ac_check_headers) foreach(arg ${ARGN}) check_include_file ("${arg}" FOUND_${arg}) @@ -61,8 +65,8 @@ endfunction() ac_check_headers ( sys/types.h sys/stat.h sys/filio.h sys/sockio.h sys/utime.h sys/un.h sys/syscall.h sys/uio.h sys/param.h sys/sysctl.h sys/prctl.h sys/socket.h sys/utsname.h sys/select.h sys/user.h sys/poll.h sys/wait.h sts/auxv.h sys/resource.h - sys/ioctl.h sys/errno.h sys/sendfile.h sys/statvfs.h sys/statfs.h sys/mman.h sys/mount.h sys/time.h sys/random.h sys/mman.h - strings.h stdint.h unistd.h netdb.h utime.h semaphore.h libproc.h alloca.h ucontext.h pwd.h elf.h + sys/ioctl.h sys/errno.h sys/sendfile.h sys/statvfs.h sys/statfs.h sys/mman.h sys/mount.h sys/time.h sys/random.h + strings.h stdint.h unistd.h signal.h setjmp.h syslog.h netdb.h utime.h semaphore.h libproc.h alloca.h ucontext.h pwd.h elf.h gnu/lib-names.h netinet/tcp.h netinet/in.h link.h arpa/inet.h unwind.h poll.h wchar.h linux/magic.h android/legacy_signal_inlines.h android/ndk-version.h execinfo.h pthread.h pthread_np.h net/if.h dirent.h CommonCrypto/CommonDigest.h dlfcn.h getopt.h pwd.h iconv.h alloca.h @@ -77,7 +81,7 @@ ac_check_funcs ( fork execv execve waitpid localtime_r mkdtemp getrandom execvp strlcpy stpcpy strtok_r rewinddir vasprintf strndup getpwuid_r getprotobyname getprotobyname_r getaddrinfo mach_absolute_time gethrtime read_real_time gethostbyname gethostbyname2 getnameinfo getifaddrs - access inet_ntop Qp2getifaddrs) + access inet_ntop Qp2getifaddrs getpid mktemp) if(NOT HOST_DARWIN) # getentropy was introduced in macOS 10.12 / iOS 10.0 @@ -181,3 +185,14 @@ if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") set(HAVE_SYS_SYSCTL_H 1) set(HAVE_SYS_USER_H 1) endif() + +if(CMAKE_SYSTEM_NAME STREQUAL "WASI") + # Redirected to errno.h + set(SYS_ERRNO_H 0) + # Some headers exist, but don't compile (wasi sdk 12.0) + set(HAVE_SYS_SOCKET_H 0) + set(HAVE_SYS_UN_H 0) + set(HAVE_NETINET_IN_H 0) + set(HAVE_NETINET_TCP_H 0) + set(HAVE_ARPA_INET_H 0) +endif() diff --git a/src/mono/mono/component/debugger-agent.c b/src/mono/mono/component/debugger-agent.c index 7eebeb2455808..56660312cb1e5 100644 --- a/src/mono/mono/component/debugger-agent.c +++ b/src/mono/mono/component/debugger-agent.c @@ -79,6 +79,7 @@ #include #include #include +#include #include #include "debugger-agent.h" @@ -9513,7 +9514,7 @@ create_file_to_check_memory_address (void) { if (file_check_valid_memory != -1) return; - char *file_name = g_strdup_printf ("debugger_check_valid_memory.%d", getpid()); + char *file_name = g_strdup_printf ("debugger_check_valid_memory.%d", mono_process_current_pid ()); filename_check_valid_memory = g_build_filename (g_get_tmp_dir (), file_name, (const char*)NULL); file_check_valid_memory = open(filename_check_valid_memory, O_CREAT | O_WRONLY | O_APPEND, S_IWUSR); g_free (file_name); diff --git a/src/mono/mono/component/debugger-stub.c b/src/mono/mono/component/debugger-stub.c index 1296bd2235247..224f531f79648 100644 --- a/src/mono/mono/component/debugger-stub.c +++ b/src/mono/mono/component/debugger-stub.c @@ -204,7 +204,7 @@ stub_send_enc_delta (MonoImage *image, gconstpointer dmeta_bytes, int32_t dmeta_ { } -#ifdef HOST_WASM +#ifdef HOST_BROWSER #include @@ -229,6 +229,4 @@ mono_wasm_send_dbg_command (int id, int command_set, int command, guint8* data, return false; } -#endif // HOST_WASM - - +#endif // HOST_BROWSER diff --git a/src/mono/mono/eventpipe/CMakeLists.txt b/src/mono/mono/eventpipe/CMakeLists.txt index 530abd5908667..2aef7f44378ca 100644 --- a/src/mono/mono/eventpipe/CMakeLists.txt +++ b/src/mono/mono/eventpipe/CMakeLists.txt @@ -1,6 +1,6 @@ -if(ENABLE_PERFTRACING) +include(${MONO_EVENTPIPE_SHIM_SOURCE_PATH}/gen-eventing.cmake) - include(${MONO_EVENTPIPE_SHIM_SOURCE_PATH}/gen-eventing.cmake) +if(ENABLE_PERFTRACING) if (FEATURE_PERFTRACING_PAL_TCP) add_definitions(-DENABLE_PERFTRACING_PAL_TCP) diff --git a/src/mono/mono/eventpipe/gen-eventing.cmake b/src/mono/mono/eventpipe/gen-eventing.cmake index ffb92c99cdc7b..71a0520b74099 100644 --- a/src/mono/mono/eventpipe/gen-eventing.cmake +++ b/src/mono/mono/eventpipe/gen-eventing.cmake @@ -94,5 +94,7 @@ if(ENABLE_PERFTRACING) add_custom_target(${MONO_DIAGNOSTICS_TRACING_COMPONENT_NAME}-gen-sources DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${MONO_DIAGNOSTICS_TRACING_COMPONENT_NAME}-gen-sources.timestamp ${MONO_DIAGNOSTICS_TRACING_COMPONENT_NAME}-gen-headers) - +else(ENABLE_PERFTRACING) + add_custom_target(${MONO_DIAGNOSTICS_TRACING_COMPONENT_NAME}-gen-headers) + add_custom_target(${MONO_DIAGNOSTICS_TRACING_COMPONENT_NAME}-gen-sources) endif(ENABLE_PERFTRACING) diff --git a/src/mono/mono/metadata/assembly.c b/src/mono/mono/metadata/assembly.c index 235d1645fdcc9..6f14674683a86 100644 --- a/src/mono/mono/metadata/assembly.c +++ b/src/mono/mono/metadata/assembly.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -594,7 +595,7 @@ mono_set_rootdir (void) } /* Solaris 10 style */ - str = g_strdup_printf ("/proc/%d/path/a.out", getpid ()); + str = g_strdup_printf ("/proc/%d/path/a.out", mono_process_current_pid ()); #if defined(HAVE_READLINK) s = readlink (str, buf, sizeof (buf)-1); diff --git a/src/mono/mono/metadata/object.c b/src/mono/mono/metadata/object.c index 70c967df384b5..47af9c2b0d0a7 100644 --- a/src/mono/mono/metadata/object.c +++ b/src/mono/mono/metadata/object.c @@ -7851,7 +7851,7 @@ format_cmd_line (int argc, char **argv, gboolean add_host) GString *cmd_line = NULL; if (add_host) { -#if !defined(HOST_WIN32) && defined(HAVE_UNISTD_H) +#if !defined(HOST_WIN32) && defined(HAVE_GETPID) host_path = mono_w32process_get_path (getpid ()); #elif defined(HOST_WIN32) gunichar2 *host_path_ucs2 = NULL; diff --git a/src/mono/mono/metadata/w32file-unix.c b/src/mono/mono/metadata/w32file-unix.c index e11bfb335dac5..50da28bf8468b 100644 --- a/src/mono/mono/metadata/w32file-unix.c +++ b/src/mono/mono/metadata/w32file-unix.c @@ -277,93 +277,6 @@ _wapi_dirname (const gchar *filename) return ret; } -static gboolean -_wapi_lock_file_region (gint fd, off_t offset, off_t length) -{ - struct flock lock_data; - gint ret; - - if (offset < 0 || length < 0) { - mono_w32error_set_last (ERROR_INVALID_PARAMETER); - return FALSE; - } - - lock_data.l_type = F_WRLCK; - lock_data.l_whence = SEEK_SET; - lock_data.l_start = offset; - lock_data.l_len = length; - - do { - ret = fcntl (fd, F_SETLK, &lock_data); - } while(ret == -1 && errno == EINTR); - - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fcntl returns %d", __func__, ret); - - if (ret == -1) { - /* - * if locks are not available (NFS for example), - * ignore the error - */ - if (errno == ENOLCK -#ifdef EOPNOTSUPP - || errno == EOPNOTSUPP -#endif -#ifdef ENOTSUP - || errno == ENOTSUP -#endif - ) { - return TRUE; - } - - mono_w32error_set_last (ERROR_LOCK_VIOLATION); - return FALSE; - } - - return TRUE; -} - -static gboolean -_wapi_unlock_file_region (gint fd, off_t offset, off_t length) -{ - struct flock lock_data; - gint ret; - - lock_data.l_type = F_UNLCK; - lock_data.l_whence = SEEK_SET; - lock_data.l_start = offset; - lock_data.l_len = length; - - do { - ret = fcntl (fd, F_SETLK, &lock_data); - } while(ret == -1 && errno == EINTR); - - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fcntl returns %d", __func__, ret); - - if (ret == -1) { - /* - * if locks are not available (NFS for example), - * ignore the error - */ - if (errno == ENOLCK -#ifdef EOPNOTSUPP - || errno == EOPNOTSUPP -#endif -#ifdef ENOTSUP - || errno == ENOTSUP -#endif - ) { - return TRUE; - } - - mono_w32error_set_last (ERROR_LOCK_VIOLATION); - return FALSE; - } - - return TRUE; -} - -static gboolean lock_while_writing = FALSE; - static void _wapi_set_last_error_from_errno (void) { @@ -402,7 +315,6 @@ static gboolean file_write (FileHandle *filehandle, gpointer buffer, guint32 numbytes, guint32 *byteswritten) { gint ret; - off_t current_pos = 0; MonoThreadInfo *info = mono_thread_info_current (); if(byteswritten!=NULL) { @@ -415,26 +327,6 @@ file_write (FileHandle *filehandle, gpointer buffer, guint32 numbytes, guint32 * mono_w32error_set_last (ERROR_ACCESS_DENIED); return(FALSE); } - - if (lock_while_writing) { - /* Need to lock the region we're about to write to, - * because we only do advisory locking on POSIX - * systems - */ - MONO_ENTER_GC_SAFE; - current_pos = lseek (((MonoFDHandle*) filehandle)->fd, (off_t)0, SEEK_CUR); - MONO_EXIT_GC_SAFE; - if (current_pos == -1) { - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_FILE, "%s: fd %d lseek failed: %s", __func__, ((MonoFDHandle*) filehandle)->fd, g_strerror (errno)); - _wapi_set_last_error_from_errno (); - return(FALSE); - } - - if (_wapi_lock_file_region (((MonoFDHandle*) filehandle)->fd, current_pos, numbytes) == FALSE) { - /* The error has already been set */ - return(FALSE); - } - } do { MONO_ENTER_GC_SAFE; @@ -442,10 +334,6 @@ file_write (FileHandle *filehandle, gpointer buffer, guint32 numbytes, guint32 * MONO_EXIT_GC_SAFE; } while (ret == -1 && errno == EINTR && !mono_thread_info_is_interrupt_state (info)); - - if (lock_while_writing) { - _wapi_unlock_file_region (((MonoFDHandle*) filehandle)->fd, current_pos, numbytes); - } if (ret == -1) { if (errno == EINTR) { @@ -650,9 +538,6 @@ mono_w32file_init (void) mono_fdhandle_register (MONO_FDTYPE_PIPE, &file_data_callbacks); mono_coop_mutex_init (&file_share_mutex); - - if (g_hasenv ("MONO_STRICT_IO_EMULATION")) - lock_while_writing = TRUE; } gpointer diff --git a/src/mono/mono/mini/mini-wasm.c b/src/mono/mono/mini/mini-wasm.c index 56d58e1c6a0d2..f822af02a1d2c 100644 --- a/src/mono/mono/mini/mini-wasm.c +++ b/src/mono/mono/mini/mini-wasm.c @@ -11,9 +11,6 @@ #include #include -//XXX This is dirty, extend ee.h to support extracting info from MonoInterpFrameHandle -#include - static int mono_wasm_debug_level = 0; #ifndef DISABLE_JIT @@ -383,7 +380,7 @@ mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_targe g_error ("mono_arch_get_delegate_invoke_impl"); } -#ifdef HOST_WASM +#ifdef HOST_BROWSER #include @@ -398,7 +395,7 @@ G_END_DECLS void mono_background_exec (void); -#endif // HOST_WASM +#endif // HOST_BROWSER gpointer mono_arch_get_this_arg_from_call (host_mgreg_t *regs, guint8 *code) @@ -496,7 +493,7 @@ mono_arch_context_get_int_reg_address (MonoContext *ctx, int reg) return 0; } -#ifdef HOST_WASM +#ifdef HOST_BROWSER void mono_runtime_setup_stat_profiler (void) @@ -566,7 +563,7 @@ mono_set_timeout_exec (int id) void mono_wasm_set_timeout (int timeout, int id) { -#ifdef HOST_WASM +#ifdef HOST_BROWSER mono_set_timeout (timeout, id); #endif } @@ -599,7 +596,7 @@ tp_cb (void) } } -#ifdef HOST_WASM +#ifdef HOST_BROWSER void mono_wasm_queue_tp_cb (void) { @@ -610,7 +607,7 @@ mono_wasm_queue_tp_cb (void) void mono_arch_register_icall (void) { -#ifdef HOST_WASM +#ifdef HOST_BROWSER mono_add_internal_call_internal ("System.Threading.TimerQueue::SetTimeout", mono_wasm_set_timeout); mono_add_internal_call_internal ("System.Threading.ThreadPool::QueueCallback", mono_wasm_queue_tp_cb); #endif @@ -622,7 +619,7 @@ mono_arch_patch_code_new (MonoCompile *cfg, guint8 *code, MonoJumpInfo *ji, gpoi g_error ("mono_arch_patch_code_new"); } -#ifdef HOST_WASM +#ifdef HOST_BROWSER G_BEGIN_DECLS @@ -755,7 +752,7 @@ mono_wasm_print_stack_trace (void) ); } -#endif // HOST_WASM +#endif // HOST_BROWSER gpointer mono_arch_load_function (MonoJitICallId jit_icall_id) diff --git a/src/mono/mono/sgen/sgen-gc.c b/src/mono/mono/sgen/sgen-gc.c index d5b31e753179c..08f192c49e7c2 100644 --- a/src/mono/mono/sgen/sgen-gc.c +++ b/src/mono/mono/sgen/sgen-gc.c @@ -2719,7 +2719,7 @@ sgen_perform_collection_inner (size_t requested_size, int generation_to_collect, sgen_restart_world (oldest_generation_collected, forced_serial || !sgen_major_collector.is_concurrent); } -#ifdef HOST_WASM +#ifdef HOST_BROWSER typedef struct { size_t requested_size; @@ -2737,16 +2737,14 @@ gc_pump_callback (void) sgen_perform_collection_inner (gc_request.requested_size, gc_request.generation_to_collect, gc_request.reason, TRUE, TRUE); gc_request.generation_to_collect = 0; } -#endif -#ifdef HOST_WASM extern gboolean mono_wasm_enable_gc; #endif void sgen_perform_collection (size_t requested_size, int generation_to_collect, const char *reason, gboolean forced_serial, gboolean stw) { -#ifdef HOST_WASM +#ifdef HOST_BROWSER if (!mono_wasm_enable_gc) { g_assert (stw); //can't handle non-stw mode (IE, domain unload) //we ignore forced_serial diff --git a/src/mono/mono/utils/mono-mmap-wasm.c b/src/mono/mono/utils/mono-mmap-wasm.c index e8cada0137b92..b04d69ab1b5e2 100644 --- a/src/mono/mono/utils/mono-mmap-wasm.c +++ b/src/mono/mono/utils/mono-mmap-wasm.c @@ -233,7 +233,7 @@ void* mono_shared_area (void) { if (!malloced_shared_area) - malloced_shared_area = mono_malloc_shared_area (getpid ()); + malloced_shared_area = mono_malloc_shared_area (mono_process_current_pid ()); /* get the pid here */ return malloced_shared_area; } diff --git a/src/mono/mono/utils/mono-proclib.c b/src/mono/mono/utils/mono-proclib.c index 1b19cdec49fb5..1fe731d9fe0f5 100644 --- a/src/mono/mono/utils/mono-proclib.c +++ b/src/mono/mono/utils/mono-proclib.c @@ -742,8 +742,10 @@ mono_process_get_data (gpointer pid, MonoProcessData data) int mono_process_current_pid () { -#if defined(HAVE_UNISTD_H) +#if defined(HAVE_GETPID) return (int) getpid (); +#elif defined(HOST_WASI) + return 0; #else #error getpid #endif diff --git a/src/mono/mono/utils/mono-signal-handler.h b/src/mono/mono/utils/mono-signal-handler.h index 0b2b38482b257..151c729928508 100644 --- a/src/mono/mono/utils/mono-signal-handler.h +++ b/src/mono/mono/utils/mono-signal-handler.h @@ -89,6 +89,8 @@ typedef struct { EXCEPTION_POINTERS* ep; } MonoWindowsSigHandlerInfo; /* seh_vectored_exception_handler () passes in a CONTEXT* */ +#elif defined(HOST_WASM) +#define MONO_SIG_HANDLER_INFO_TYPE int #else /* sigaction */ #define MONO_SIG_HANDLER_INFO_TYPE siginfo_t diff --git a/src/mono/mono/utils/mono-threads-wasm.c b/src/mono/mono/utils/mono-threads-wasm.c index dc792e8158e00..fae0193050b9c 100644 --- a/src/mono/mono/utils/mono-threads-wasm.c +++ b/src/mono/mono/utils/mono-threads-wasm.c @@ -9,9 +9,12 @@ #include #include +#include + +#ifdef HOST_BROWSER + #include #include -#include #define round_down(addr, val) ((void*)((addr) & ~((val) - 1))) @@ -29,6 +32,24 @@ wasm_get_stack_size (void) return (guint8*)emscripten_stack_get_base () - (guint8*)emscripten_stack_get_end (); } +#else /* WASI */ + +static int +wasm_get_stack_base (void) +{ + g_assert_not_reached (); + return 0; +} + +static int +wasm_get_stack_size (void) +{ + g_assert_not_reached (); + return 0; +} + +#endif + int mono_thread_info_get_system_max_stack_size (void) { @@ -277,6 +298,13 @@ mono_threads_platform_in_critical_region (THREAD_INFO_TYPE *info) return FALSE; } +void +mono_memory_barrier_process_wide (void) +{ +} + +#ifdef HOST_BROWSER + G_EXTERN_C extern void schedule_background_exec (void); @@ -310,10 +338,7 @@ mono_background_exec (void) g_slist_free (j); } -void -mono_memory_barrier_process_wide (void) -{ -} +#endif /* HOST_BROWSER */ #else From 6e0e2d05ba3af1377aac1da448cf086e544ce2a5 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 8 Oct 2021 14:45:31 -0700 Subject: [PATCH 018/106] Fix some of the issues around pgo data collection of stack trace data (#59970) - Improve assembly name acquisition in dotnet-pgo - Use assembly name from AssemblyLoad event, and do not attempt to figure it out from the Module load event - Collect MemoryRegionInfo for all method regions, not just the first. - Add a concept to check a guid associated with the associated dll files to avoid data tearing - Improve parsing of instantiated method signatures when reading an R2R file - Handle the subtly different format which is use for type signatures in the InstantiationMethodSignature portion of an R2R file --- .../ReadyToRunReader.cs | 3 +- .../tools/dotnet-pgo/MethodMemoryMap.cs | 100 ++++++++++++++---- .../tools/dotnet-pgo/ModuleLoadLogger.cs | 19 +++- src/coreclr/tools/dotnet-pgo/Program.cs | 76 +++++++++++-- .../dotnet-pgo/R2RSignatureTypeProvider.cs | 37 ++++++- .../TraceRuntimeDescToTypeSystemDesc.cs | 34 +++--- .../dotnet-pgo/TraceTypeSystemContext.cs | 45 ++++++-- 7 files changed, 250 insertions(+), 64 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs index 197e04c33e380..a8962fa2e3c1d 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs @@ -877,7 +877,7 @@ private void ParseInstanceMethodEntrypointsCustom(provider, default(TGenericContext), mdReader.MetadataReader, this, (int)curParser.Offset); + var decoder = new R2RSignatureDecoder(provider, default(TGenericContext), mdReader?.MetadataReader, this, (int)curParser.Offset); TMethod customMethod = decoder.ParseMethod(); @@ -887,7 +887,6 @@ private void ParseInstanceMethodEntrypointsCustom + { + public JittedID(long methodID, long reJITID) + { + MethodID = methodID; + ReJITID = reJITID; + } + + public readonly long MethodID; + public readonly long ReJITID; + + public override int GetHashCode() => HashCode.Combine(MethodID, ReJITID); + public override bool Equals([NotNullWhen(true)] object obj) => obj is JittedID id ? Equals(id) : false; + + public bool Equals(JittedID other) + { + if (other.MethodID != MethodID) + return false; + return other.ReJITID == ReJITID; + } + } + public MethodMemoryMap( TraceProcess p, TraceTypeSystemContext tsc, TraceRuntimeDescToTypeSystemDesc idParser, - int clrInstanceID) + int clrInstanceID, + Logger logger) { // Capture the addresses of jitted code List infos = new List(); - Dictionary info = new Dictionary(); + Dictionary info = new Dictionary(); foreach (var e in p.EventsInProcess.ByEventType()) { if (e.ClrInstanceID != clrInstanceID) @@ -47,13 +71,16 @@ public MethodMemoryMap( if (method != null) { - infos.Add(new MemoryRegionInfo + JittedID jittedID = new JittedID(e.MethodID, 0); + if (!info.ContainsKey(jittedID)) { - StartAddress = e.MethodStartAddress, - EndAddress = e.MethodStartAddress + checked((uint)e.MethodSize), - MethodID = e.MethodID, - Method = method, - }); + info.Add(jittedID, new MemoryRegionInfo + { + StartAddress = e.MethodStartAddress, + EndAddress = e.MethodStartAddress + checked((uint)e.MethodSize), + Method = method, + }); + } } } @@ -67,7 +94,7 @@ public MethodMemoryMap( MethodDesc method = null; try { - method = idParser.ResolveMethodID(e.MethodID); + method = idParser.ResolveMethodID(e.MethodID, throwIfNotFound: false); } catch { @@ -75,17 +102,20 @@ public MethodMemoryMap( if (method != null) { - infos.Add(new MemoryRegionInfo + JittedID jittedID = new JittedID(e.MethodID, e.ReJITID); + if (!info.ContainsKey(jittedID)) { - StartAddress = e.MethodStartAddress, - EndAddress = e.MethodStartAddress + checked((uint)e.MethodSize), - MethodID = e.MethodID, - Method = method, - }); + info.Add(jittedID, new MemoryRegionInfo + { + StartAddress = e.MethodStartAddress, + EndAddress = e.MethodStartAddress + checked((uint)e.MethodSize), + Method = method, + }); + } } } - var sigProvider = new R2RSignatureTypeProvider(tsc); + var sigProvider = new R2RSignatureTypeProviderForGlobalTables(tsc); foreach (var module in p.LoadedModules) { if (module.FilePath == "") @@ -120,18 +150,40 @@ public MethodMemoryMap( } } } - catch { } + catch + { + logger.PrintWarning($"Failed to load method entry points from R2R module {module.FilePath}"); + } } - // Can have duplicate events, so pick first for each - var byMethodID = infos.GroupBy(i => i.MethodID).ToDictionary(g => g.Key, g => g.First()); + // Associate NativeToILMap with MethodLoad event found Memory Regions foreach (MethodILToNativeMapTraceData e in p.EventsInProcess.ByEventType()) { - if (byMethodID.TryGetValue(e.MethodID, out MemoryRegionInfo inf)) + if (info.TryGetValue(new JittedID(e.MethodID, e.ReJITID), out MemoryRegionInfo inf)) inf.NativeToILMap = NativeToILMap.FromEvent(e); } - _infos = byMethodID.Values.OrderBy(i => i.StartAddress).ToArray(); + // Sort the R2R data by StartAddress + MemoryRegionInfoStartAddressComparer startAddressComparer = new MemoryRegionInfoStartAddressComparer(); + infos.Sort(startAddressComparer); + + // For each method found via MethodLoad events, check to see if it exists in the infos array, and if it does not, build a list to add + List memoryRegionsToAdd = new List(); + foreach (var methodLoadInfo in info.Values) + { + int searchResult = infos.BinarySearch(methodLoadInfo, startAddressComparer); + if (searchResult < 0) + { + memoryRegionsToAdd.Add(methodLoadInfo); + } + } + + // Add the regions from the MethodLoad events, and keep the overall array sorted + infos.AddRange(memoryRegionsToAdd); + infos.Sort(startAddressComparer); + + _infos = infos.ToArray(); + _infoKeys = _infos.Select(i => i.StartAddress).ToArray(); #if DEBUG @@ -164,13 +216,17 @@ public MemoryRegionInfo GetInfo(ulong ip) } public MethodDesc GetMethod(ulong ip) => GetInfo(ip)?.Method; + + private class MemoryRegionInfoStartAddressComparer : IComparer + { + int IComparer.Compare(MemoryRegionInfo x, MemoryRegionInfo y) => x.StartAddress.CompareTo(y.StartAddress); + } } public class MemoryRegionInfo { public ulong StartAddress { get; set; } public ulong EndAddress { get; set; } - public long MethodID { get; set; } public MethodDesc Method { get; set; } public NativeToILMap NativeToILMap { get; set; } } diff --git a/src/coreclr/tools/dotnet-pgo/ModuleLoadLogger.cs b/src/coreclr/tools/dotnet-pgo/ModuleLoadLogger.cs index fb9afffb517ce..0271bb930b877 100644 --- a/src/coreclr/tools/dotnet-pgo/ModuleLoadLogger.cs +++ b/src/coreclr/tools/dotnet-pgo/ModuleLoadLogger.cs @@ -18,13 +18,28 @@ public ModuleLoadLogger(Logger logger) Logger _logger; + [ThreadStatic] + private static int s_loadFailuresAreErrors = 0; + + public class LoadFailuresAsErrors : IDisposable + { + public LoadFailuresAsErrors() + { + s_loadFailuresAreErrors++; + } + public void Dispose() + { + s_loadFailuresAreErrors--; + } + } + public void LogModuleLoadFailure(string simpleName, string filePath) { if (_simpleNamesReported.Add(simpleName)) { string str = $"Failed to load assembly '{simpleName}' from '{filePath}'"; - if (String.Compare("System.Private.CoreLib", simpleName, StringComparison.OrdinalIgnoreCase) == 0) + if (s_loadFailuresAreErrors != 0 || String.Compare("System.Private.CoreLib", simpleName, StringComparison.OrdinalIgnoreCase) == 0) { _logger.PrintError(str); } @@ -41,7 +56,7 @@ public void LogModuleLoadFailure(string simpleName) { string str = $"Failed to load assembly '{simpleName}'"; - if (String.Compare("System.Private.CoreLib", simpleName, StringComparison.OrdinalIgnoreCase) == 0) + if (s_loadFailuresAreErrors != 0 || String.Compare("System.Private.CoreLib", simpleName, StringComparison.OrdinalIgnoreCase) == 0) { _logger.PrintError(str); } diff --git a/src/coreclr/tools/dotnet-pgo/Program.cs b/src/coreclr/tools/dotnet-pgo/Program.cs index 77113377c478d..4636a4184a509 100644 --- a/src/coreclr/tools/dotnet-pgo/Program.cs +++ b/src/coreclr/tools/dotnet-pgo/Program.cs @@ -948,9 +948,11 @@ static int InnerProcessTraceFileMain(CommandLineOptions commandLineOptions) return -5; } - if (!p.EventsInProcess.ByEventType().Any()) + if (!p.EventsInProcess.ByEventType().Any() && + !p.EventsInProcess.ByEventType().Any() && + !p.EventsInProcess.ByEventType< SampledProfileTraceData>().Any()) { - PrintError($"No managed jit starting data\nWas the trace collected with provider at least \"Microsoft-Windows-DotNETRuntime:0x4000080018:5\"?"); + PrintError($"No data in trace for process\nWas the trace collected with provider at least \"Microsoft-Windows-DotNETRuntime:0x4000080018:5\"?"); return -5; } @@ -1014,7 +1016,9 @@ static int InnerProcessTraceFileMain(CommandLineOptions commandLineOptions) filePathError = true; } else - tsc.GetModuleFromPath(fileReference.FullName); + { + tsc.GetModuleFromPath(fileReference.FullName, throwIfNotLoadable: false); + } } catch (Internal.TypeSystem.TypeSystemException.BadImageFormatException) { @@ -1034,7 +1038,63 @@ static int InnerProcessTraceFileMain(CommandLineOptions commandLineOptions) TraceRuntimeDescToTypeSystemDesc idParser = new TraceRuntimeDescToTypeSystemDesc(p, tsc, clrInstanceId.Value); - SortedDictionary methodsToAttemptToPrepare = new SortedDictionary(); + int mismatchErrors = 0; + foreach (var e in p.EventsInProcess.ByEventType()) + { + ModuleDesc loadedModule = idParser.ResolveModuleID(e.ModuleID, false); + if (loadedModule == null) + { + PrintWarning($"Unable to find loaded module {e.ModuleILFileName} to verify match"); + continue; + } + + EcmaModule ecmaModule = loadedModule as EcmaModule; + if (ecmaModule == null) + { + continue; + } + + bool matched = false; + bool mismatch = false; + foreach (var debugEntry in ecmaModule.PEReader.ReadDebugDirectory()) + { + if (debugEntry.Type == DebugDirectoryEntryType.CodeView) + { + var codeViewData = ecmaModule.PEReader.ReadCodeViewDebugDirectoryData(debugEntry); + if (codeViewData.Path.EndsWith("ni.pdb")) + continue; + if (codeViewData.Guid != e.ManagedPdbSignature) + { + PrintError($"Dll mismatch between assembly located at \"{e.ModuleILPath}\" during trace collection and module \"{tsc.PEReaderToFilePath(ecmaModule.PEReader)}\""); + mismatchErrors++; + mismatch = true; + continue; + } + else + { + matched = true; + } + } + } + + if (!matched && !mismatch) + { + PrintMessage($"Unable to validate match between assembly located at \"{e.ModuleILPath}\" during trace collection and module \"{tsc.PEReaderToFilePath(ecmaModule.PEReader)}\""); + } + + // TODO find some way to match on MVID as only some dlls have managed pdbs, and this won't find issues with embedded pdbs + } + + if (mismatchErrors != 0) + { + PrintError($"{mismatchErrors} mismatch error(s) found"); + return -1; + } + + // Now that the modules are validated run Init to prepare for the rest of execution + idParser.Init(); + + SortedDictionary methodsToAttemptToPrepare = new SortedDictionary(); if (commandLineOptions.ProcessR2REvents) { @@ -1133,7 +1193,8 @@ MethodMemoryMap GetMethodMemMap() p, tsc, idParser, - clrInstanceId.Value); + clrInstanceId.Value, + s_logger); } return methodMemMap; @@ -1155,6 +1216,9 @@ MethodMemoryMap GetMethodMemMap() MethodMemoryMap mmap = GetMethodMemMap(); foreach (var e in p.EventsInProcess.ByEventType()) { + if ((e.TimeStampRelativeMSec < commandLineOptions.ExcludeEventsBefore) && (e.TimeStampRelativeMSec > commandLineOptions.ExcludeEventsAfter)) + continue; + var callstack = e.CallStack(); if (callstack == null) continue; @@ -1191,7 +1255,7 @@ MethodMemoryMap GetMethodMemMap() if (!methodsListedToPrepare.Contains(nextMethod)) { methodsListedToPrepare.Add(nextMethod); - methodsToAttemptToPrepare.Add((int)e.EventIndex, new ProcessedMethodData(e.TimeStampRelativeMSec, nextMethod, "SampleMethodCaller")); + methodsToAttemptToPrepare.Add(0x100000000 + (int)e.EventIndex, new ProcessedMethodData(e.TimeStampRelativeMSec, nextMethod, "SampleMethodCaller")); } if (!callGraph.TryGetValue(nextMethod, out var innerDictionary)) diff --git a/src/coreclr/tools/dotnet-pgo/R2RSignatureTypeProvider.cs b/src/coreclr/tools/dotnet-pgo/R2RSignatureTypeProvider.cs index a640425f209b4..3f68a63e271b3 100644 --- a/src/coreclr/tools/dotnet-pgo/R2RSignatureTypeProvider.cs +++ b/src/coreclr/tools/dotnet-pgo/R2RSignatureTypeProvider.cs @@ -1,7 +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; using System.Collections.Immutable; +using System.Diagnostics; using System.Linq; using System.Reflection.Metadata; using ILCompiler.Reflection.ReadyToRun; @@ -109,7 +111,7 @@ MethodDesc IR2RSignatureTypeProvider.GetMethodFromMethodDef(MetadataReader reader, MethodDefinitionHandle handle, TypeDesc owningTypeOverride) + protected MethodDesc GetMethodFromMethodDef(MetadataReader reader, MethodDefinitionHandle handle, TypeDesc owningTypeOverride) { var ecmaModule = (EcmaModule)_tsc.GetModuleForSimpleName(reader.GetString(reader.GetAssemblyDefinition().Name)); var method = (MethodDesc)ecmaModule.GetObject(handle, NotFoundBehavior.ReturnNull); @@ -119,11 +121,19 @@ MethodDesc IR2RSignatureTypeProvider.GetMethodFromMethodDef(MetadataReader reader, MethodDefinitionHandle handle, TypeDesc owningTypeOverride) + { + return GetMethodFromMethodDef(reader, handle, owningTypeOverride); + } + MethodDesc IR2RSignatureTypeProvider.GetMethodWithFlags(ReadyToRunMethodSigFlags flags, MethodDesc method) { return method; @@ -237,4 +247,27 @@ TypeDesc ISignatureTypeProvider.GetTypeFromSpec return (TypeDesc)ecmaModule.GetObject(handle, NotFoundBehavior.ReturnNull); } } + + class R2RSignatureTypeProviderForGlobalTables : R2RSignatureTypeProvider, IR2RSignatureTypeProvider + { + public R2RSignatureTypeProviderForGlobalTables(TraceTypeSystemContext tsc) : base(tsc) + { + } + + MethodDesc IR2RSignatureTypeProvider.GetMethodFromMethodDef(MetadataReader reader, MethodDefinitionHandle handle, TypeDesc owningTypeOverride) + { + if (owningTypeOverride != null) + { + reader = ((EcmaModule)((MetadataType)owningTypeOverride.GetTypeDefinition()).Module).MetadataReader; + } + Debug.Assert(reader != null); + return GetMethodFromMethodDef(reader, handle, owningTypeOverride); + } + + MethodDesc IR2RSignatureTypeProvider.GetMethodFromMemberRef(MetadataReader reader, MemberReferenceHandle handle, TypeDesc owningTypeOverride) + { + // Global signature cannot have MemberRef entries in them as such things aren't uniquely identifiable + throw new NotSupportedException(); + } + } } diff --git a/src/coreclr/tools/dotnet-pgo/TraceRuntimeDescToTypeSystemDesc.cs b/src/coreclr/tools/dotnet-pgo/TraceRuntimeDescToTypeSystemDesc.cs index cb1816a26e073..90e4e96c37310 100644 --- a/src/coreclr/tools/dotnet-pgo/TraceRuntimeDescToTypeSystemDesc.cs +++ b/src/coreclr/tools/dotnet-pgo/TraceRuntimeDescToTypeSystemDesc.cs @@ -93,15 +93,15 @@ public override string ToString() class ModuleDescInfo { - public ModuleDescInfo(long id, TraceManagedModule traceManagedModule) + public ModuleDescInfo(long id, string assemblyName) { ID = id; - TraceManagedModule = traceManagedModule; + AssemblyName = assemblyName; } public readonly long ID; public ModuleDesc Module; - public readonly TraceManagedModule TraceManagedModule; + public readonly string AssemblyName; } private readonly Dictionary _methods = new Dictionary(); @@ -222,9 +222,11 @@ public TraceRuntimeDescToTypeSystemDesc(TraceProcess traceProcess, TypeSystemCon } Dictionary assemblyToCLRInstanceIDMap = new Dictionary(); + Dictionary assemblyToFullyQualifiedAssemblyName = new Dictionary(); foreach (var assemblyLoadTrace in _traceProcess.EventsInProcess.ByEventType()) { assemblyToCLRInstanceIDMap[assemblyLoadTrace.AssemblyID] = assemblyLoadTrace.ClrInstanceID; + assemblyToFullyQualifiedAssemblyName[assemblyLoadTrace.AssemblyID] = assemblyLoadTrace.FullyQualifiedAssemblyName; } foreach (var moduleFile in _traceProcess.LoadedModules) @@ -240,20 +242,15 @@ public TraceRuntimeDescToTypeSystemDesc(TraceProcess traceProcess, TypeSystemCon if (clrInstanceIDModule != _clrInstanceID) continue; - if (managedModule.ModuleFile != null) - { - ModuleDescInfo currentInfo; - if (_modules.TryGetValue(managedModule.ModuleID, out currentInfo)) - { - continue; - } - currentInfo = new ModuleDescInfo(managedModule.ModuleID, managedModule); - _modules.Add(managedModule.ModuleID, currentInfo); - } + var currentInfo = new ModuleDescInfo(managedModule.ModuleID, assemblyToFullyQualifiedAssemblyName[managedModule.AssemblyID]); + _modules.Add(managedModule.ModuleID, currentInfo); } } + } - + // Call before any api other than ResolveModuleID will work + public void Init() + { // Fill in all the types foreach (var entry in _types) { @@ -271,14 +268,7 @@ public ModuleDesc ResolveModuleID(long handle, bool throwIfNotFound = true) if (minfo.Module != null) return minfo.Module; - string simpleName = minfo.TraceManagedModule.Name; - - if (!File.Exists(minfo.TraceManagedModule.FilePath) && minfo.TraceManagedModule.FilePath.EndsWith(".il.dll") && simpleName.EndsWith(".il")) - { - simpleName = simpleName.Substring(0, simpleName.Length - 3); - } - - minfo.Module = _context.ResolveAssembly(new AssemblyName(simpleName), throwIfNotFound); + minfo.Module = _context.ResolveAssembly(new AssemblyName(minfo.AssemblyName), throwIfNotFound); return minfo.Module; } else diff --git a/src/coreclr/tools/dotnet-pgo/TraceTypeSystemContext.cs b/src/coreclr/tools/dotnet-pgo/TraceTypeSystemContext.cs index 3f0155fc5b76c..09e2f52ea053a 100644 --- a/src/coreclr/tools/dotnet-pgo/TraceTypeSystemContext.cs +++ b/src/coreclr/tools/dotnet-pgo/TraceTypeSystemContext.cs @@ -16,6 +16,7 @@ using Microsoft.Diagnostics.Tracing.Parsers.Clr; using System.Reflection.Metadata; using ILCompiler.Reflection.ReadyToRun; +using System.Runtime.CompilerServices; namespace Microsoft.Diagnostics.Tools.Pgo { @@ -181,9 +182,9 @@ public ModuleDesc GetModuleForSimpleName(string simpleName, bool throwIfNotFound } } - public EcmaModule GetModuleFromPath(string filePath) + public EcmaModule GetModuleFromPath(string filePath, bool throwIfNotLoadable = true) { - return GetOrAddModuleFromPath(filePath, null, true); + return GetOrAddModuleFromPath(filePath, null, true, throwIfNotLoadable: throwIfNotLoadable); } public EcmaModule GetMetadataOnlyModuleFromPath(string filePath) @@ -196,7 +197,7 @@ public EcmaModule GetMetadataOnlyModuleFromMemory(string filePath, byte[] module return GetOrAddModuleFromPath(filePath, moduleData, false); } - private EcmaModule GetOrAddModuleFromPath(string filePath, byte[] moduleData, bool useForBinding) + private EcmaModule GetOrAddModuleFromPath(string filePath, byte[] moduleData, bool useForBinding, bool throwIfNotLoadable = true) { // This method is not expected to be called frequently. Linear search is acceptable. foreach (var entry in ModuleHashtable.Enumerator.Get(_moduleHashtable)) @@ -208,10 +209,13 @@ private EcmaModule GetOrAddModuleFromPath(string filePath, byte[] moduleData, bo bool succeeded = false; try { - EcmaModule returnValue = AddModule(filePath, null, moduleData, useForBinding); - _moduleLoadLogger.LogModuleLoadSuccess(returnValue.Assembly.GetName().Name, filePath); - succeeded = true; - return returnValue; + EcmaModule returnValue = AddModule(filePath, null, moduleData, useForBinding, throwIfNotLoadable: throwIfNotLoadable); + if (returnValue != null) + { + _moduleLoadLogger.LogModuleLoadSuccess(returnValue.Assembly.GetName().Name, filePath); + succeeded = true; + return returnValue; + } } finally { @@ -220,6 +224,20 @@ private EcmaModule GetOrAddModuleFromPath(string filePath, byte[] moduleData, bo _moduleLoadLogger.LogModuleLoadFailure(Path.GetFileNameWithoutExtension(filePath), filePath); } } + return null; + } + + private static ConditionalWeakTable s_peReaderToPath = new ConditionalWeakTable(); + + // Get the file path used to load a PEReader or "Memory" if it wasn't loaded from a file + public string PEReaderToFilePath(PEReader reader) + { + if (!s_peReaderToPath.TryGetValue(reader, out string filepath)) + { + filepath = "Memory"; + } + + return filepath; } public static unsafe PEReader OpenPEFile(string filePath, byte[] moduleBytes, out MemoryMappedViewAccessor mappedViewAccessor) @@ -249,6 +267,7 @@ public static unsafe PEReader OpenPEFile(string filePath, byte[] moduleBytes, ou var safeBuffer = accessor.SafeMemoryMappedViewHandle; var peReader = new PEReader((byte*)safeBuffer.DangerousGetHandle(), (int)safeBuffer.ByteLength); + s_peReaderToPath.Add(peReader, filePath); // MemoryMappedFile does not need to be kept around. MemoryMappedViewAccessor is enough. @@ -268,13 +287,17 @@ public static unsafe PEReader OpenPEFile(string filePath, byte[] moduleBytes, ou } } - private EcmaModule AddModule(string filePath, string expectedSimpleName, byte[] moduleDataBytes, bool useForBinding) + private EcmaModule AddModule(string filePath, string expectedSimpleName, byte[] moduleDataBytes, bool useForBinding, bool throwIfNotLoadable = true) { MemoryMappedViewAccessor mappedViewAccessor = null; PdbSymbolReader pdbReader = null; try { PEReader peReader = OpenPEFile(filePath, moduleDataBytes, out mappedViewAccessor); + if ((!peReader.HasMetadata) && !throwIfNotLoadable) + { + return null; + } pdbReader = OpenAssociatedSymbolFile(filePath, peReader); EcmaModule module = EcmaModule.Create(this, peReader, containingAssembly: null, pdbReader); @@ -316,6 +339,10 @@ private EcmaModule AddModule(string filePath, string expectedSimpleName, byte[] return module; } + catch when (!throwIfNotLoadable) + { + return null; + } finally { if (mappedViewAccessor != null) @@ -375,12 +402,14 @@ public MetadataStringDecoder GetMetadataStringDecoder() IAssemblyMetadata IAssemblyResolver.FindAssembly(MetadataReader metadataReader, AssemblyReferenceHandle assemblyReferenceHandle, string parentFile) { + using var triggerErrors = new ModuleLoadLogger.LoadFailuresAsErrors(); EcmaAssembly ecmaAssembly = (EcmaAssembly)this.GetModuleForSimpleName(metadataReader.GetString(metadataReader.GetAssemblyReference(assemblyReferenceHandle).Name), false); return new StandaloneAssemblyMetadata(ecmaAssembly.PEReader); } IAssemblyMetadata IAssemblyResolver.FindAssembly(string simpleName, string parentFile) { + using var triggerErrors = new ModuleLoadLogger.LoadFailuresAsErrors(); EcmaAssembly ecmaAssembly = (EcmaAssembly)this.GetModuleForSimpleName(simpleName, false); return new StandaloneAssemblyMetadata(ecmaAssembly.PEReader); } From 83ec22b63939477e2076d3406b0fec68c3aca362 Mon Sep 17 00:00:00 2001 From: Juan Hoyos <19413848+hoyosjs@users.noreply.github.com> Date: Fri, 8 Oct 2021 14:56:22 -0700 Subject: [PATCH 019/106] Mark TestHost as non-shipping (#60199) --- .../Microsoft.NETCore.TestHost.pkgproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/coreclr/.nuget/Microsoft.NETCore.TestHost/Microsoft.NETCore.TestHost.pkgproj b/src/coreclr/.nuget/Microsoft.NETCore.TestHost/Microsoft.NETCore.TestHost.pkgproj index 95d6c747fb428..6c95feffc7d0e 100644 --- a/src/coreclr/.nuget/Microsoft.NETCore.TestHost/Microsoft.NETCore.TestHost.pkgproj +++ b/src/coreclr/.nuget/Microsoft.NETCore.TestHost/Microsoft.NETCore.TestHost.pkgproj @@ -2,6 +2,8 @@ + + false CoreCLR application host for test applications. From 905a08e2bd5995b8521e8cb261879cd4d5131eae Mon Sep 17 00:00:00 2001 From: Andrew Au Date: Fri, 8 Oct 2021 14:57:21 -0700 Subject: [PATCH 020/106] Fix GetGenerationWithRange when we have a gen 2 object in the ephemeral segment (#60189) --- 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 2ae2be2d8a6a4..32943d2439738 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -43371,6 +43371,7 @@ unsigned int GCHeap::GetGenerationWithRange (Object* object, uint8_t** ppStart, } if (generation == -1) { + generation = max_generation; *ppStart = heap_segment_mem (hs); *ppAllocated = *ppReserved = generation_allocation_start (hp->generation_of (max_generation - 1)); } From 3cffddd7e4db84a54f682901adcbc60af29efe5e Mon Sep 17 00:00:00 2001 From: Parker Bibus Date: Fri, 8 Oct 2021 15:07:06 -0700 Subject: [PATCH 021/106] Rollback verify command changes. (#60203) --- eng/testing/performance/performance-setup.ps1 | 66 ++----------------- 1 file changed, 6 insertions(+), 60 deletions(-) diff --git a/eng/testing/performance/performance-setup.ps1 b/eng/testing/performance/performance-setup.ps1 index b2cf0d955c95f..ae30e9bac80ea 100644 --- a/eng/testing/performance/performance-setup.ps1 +++ b/eng/testing/performance/performance-setup.ps1 @@ -28,54 +28,6 @@ Param( [switch] $iOSLlvmBuild ) -function Verified-Move-Item { - [CmdletBinding()] - param( - [Parameter(mandatory=$true)] - [string]$Path, - [Parameter(mandatory=$true)] - [string]$Destination - ) - - Move-Item -Path $Path -Destination $Destination - if (!$?) { - Write-Output "Failed to move $Path to $Destination" - exit 1 - } -} - -function Verified-Copy-Item { - [CmdletBinding()] - param( - [Parameter(mandatory=$true)] - [string]$Path, - [Parameter(mandatory=$true)] - [string]$Destination - ) - - Copy-Item -path $Path $Destination - if (!$?) { - Write-Output "Failed to copy $Path to $Destination" - exit 1 - } -} - -function Verify-Robocopy { - [CmdletBinding()] - param( - [Parameter(mandatory=$true)] - [string]$Source - ) - - if ($LASTEXITCODE -gt 8 -or !$?) { - Write-Output "Failed to copy ${Source}: exit code $LASTEXITCODE" - exit $LASTEXITCODE - } - else if ($LASTEXITCODE -gt 1) { - Write-Output "Unusual result when copying ${Source}: exit code $LASTEXITCODE" - } -} - $RunFromPerformanceRepo = ($Repository -eq "dotnet/performance") -or ($Repository -eq "dotnet-performance") $UseCoreRun = ($CoreRootDirectory -ne [string]::Empty) $UseBaselineCoreRun = ($BaselineCoreRootDirectory -ne [string]::Empty) @@ -170,30 +122,25 @@ if ($RunFromPerformanceRepo) { $SetupArguments = "--perf-hash $CommitSha $CommonSetupArguments" robocopy $SourceDirectory $PerformanceDirectory /E /XD $PayloadDirectory $SourceDirectory\artifacts $SourceDirectory\.git - Verify-Robocopy $SourceDirectory } else { git clone --branch main --depth 1 --quiet https://github.com/dotnet/performance $PerformanceDirectory - if ($LASTEXITCODE -ne 0) { - Write-Output "git clone failed with code $LASTEXITCODE" - exit 1 - } } if($MonoDotnet -ne "") { $UsingMono = "true" $MonoDotnetPath = (Join-Path $PayloadDirectory "dotnet-mono") - Verified-Move-Item -Path $MonoDotnet -Destination $MonoDotnetPath + Move-Item -Path $MonoDotnet -Destination $MonoDotnetPath } if ($UseCoreRun) { $NewCoreRoot = (Join-Path $PayloadDirectory "Core_Root") - Verified-Move-Item -Path $CoreRootDirectory -Destination $NewCoreRoot + Move-Item -Path $CoreRootDirectory -Destination $NewCoreRoot } if ($UseBaselineCoreRun) { $NewBaselineCoreRoot = (Join-Path $PayloadDirectory "Baseline_Core_Root") - Verified-Move-Item -Path $BaselineCoreRootDirectory -Destination $NewBaselineCoreRoot + Move-Item -Path $BaselineCoreRootDirectory -Destination $NewBaselineCoreRoot } if ($AndroidMono) { @@ -201,7 +148,7 @@ if ($AndroidMono) { { mkdir $WorkItemDirectory } - Verified-Copy-Item -Path "$SourceDirectory\artifacts\bin\AndroidSampleApp\arm64\Release\android-arm64\publish\apk\bin\HelloAndroid.apk" $PayloadDirectory + Copy-Item -path "$SourceDirectory\artifacts\bin\AndroidSampleApp\arm64\Release\android-arm64\publish\apk\bin\HelloAndroid.apk" $PayloadDirectory $SetupArguments = $SetupArguments -replace $Architecture, 'arm64' } @@ -211,9 +158,9 @@ if ($iOSMono) { mkdir $WorkItemDirectory } if($iOSLlvmBuild) { - Verified-Copy-Item -Path "$SourceDirectory\iosHelloWorld\llvm" $PayloadDirectory\iosHelloWorld\llvm -Recurse + Copy-Item -path "$SourceDirectory\iosHelloWorld\llvm" $PayloadDirectory\iosHelloWorld\llvm -Recurse } else { - Verified-Copy-Item -Path "$SourceDirectory\iosHelloWorld\nollvm" $PayloadDirectory\iosHelloWorld\nollvm -Recurse + Copy-Item -path "$SourceDirectory\iosHelloWorld\nollvm" $PayloadDirectory\iosHelloWorld\nollvm -Recurse } $SetupArguments = $SetupArguments -replace $Architecture, 'arm64' @@ -221,7 +168,6 @@ if ($iOSMono) { $DocsDir = (Join-Path $PerformanceDirectory "docs") robocopy $DocsDir $WorkItemDirectory -Verify-Robocopy $DocsDir # Set variables that we will need to have in future steps $ci = $true From c929be171f85602d063cc4b66d41c374e9059368 Mon Sep 17 00:00:00 2001 From: Parker Bibus Date: Fri, 8 Oct 2021 16:00:59 -0700 Subject: [PATCH 022/106] Finish59071 update ci argsh (#60206) * Updated channel passed to ci_setup in the performance-setup.sh file. * Removed spaces from assignments so they are seen as actual variabls, and tested locally. --- eng/testing/performance/performance-setup.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/eng/testing/performance/performance-setup.sh b/eng/testing/performance/performance-setup.sh index c99ad1f48f3fa..cffd9ca8d6cdb 100755 --- a/eng/testing/performance/performance-setup.sh +++ b/eng/testing/performance/performance-setup.sh @@ -255,7 +255,11 @@ if [[ "$monoaot" == "true" ]]; then extra_benchmark_dotnet_arguments="$extra_benchmark_dotnet_arguments --category-exclusion-filter NoAOT" fi -common_setup_arguments="--channel main --queue $queue --build-number $build_number --build-configs $configurations --architecture $architecture" +cleaned_branch_name="main" +if [[ $branch == *"refs/heads/release"* ]]; then + cleaned_branch_name=${branch/refs\/heads\//} +fi +common_setup_arguments="--channel $cleaned_branch_name --queue $queue --build-number $build_number --build-configs $configurations --architecture $architecture" setup_arguments="--repository https://github.com/$repository --branch $branch --get-perf-hash --commit-sha $commit_sha $common_setup_arguments" if [[ "$run_from_perf_repo" = true ]]; then From 3de620787ab3962dcb89664a691cf917653d5bd2 Mon Sep 17 00:00:00 2001 From: Theodore Tsirpanis <12659251+teo-tsirpanis@users.noreply.github.com> Date: Sat, 9 Oct 2021 11:18:01 +0300 Subject: [PATCH 023/106] Remove Net5CompatFileStreamStrategy. (#57735) --- .../TestUtilities/System/PlatformDetection.cs | 6 - .../System.IO.FileSystem.sln | 2 - .../tests/FileStream/ReadAsync.cs | 2 +- .../tests/FileStream/SafeFileHandle.cs | 84 -- .../tests/FileStream/WriteAsync.cs | 12 - .../Net5CompatTests/Net5CompatSwitchTests.cs | 31 - ...stem.IO.FileSystem.Net5Compat.Tests.csproj | 53 - .../runtimeconfig.template.json | 5 - src/libraries/System.IO/System.IO.sln | 2 - .../System.IO.Net5Compat.Tests.csproj | 24 - .../runtimeconfig.template.json | 5 - .../System.Private.CoreLib.Shared.projitems | 4 - .../Strategies/FileStreamHelpers.Windows.cs | 7 +- .../System/IO/Strategies/FileStreamHelpers.cs | 11 +- ...StreamStrategy.CompletionSource.Windows.cs | 250 ---- .../Net5CompatFileStreamStrategy.Unix.cs | 601 --------- .../Net5CompatFileStreamStrategy.Windows.cs | 1104 ----------------- .../Net5CompatFileStreamStrategy.cs | 536 -------- 18 files changed, 6 insertions(+), 2733 deletions(-) delete mode 100644 src/libraries/System.IO.FileSystem/tests/Net5CompatTests/Net5CompatSwitchTests.cs delete mode 100644 src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj delete mode 100644 src/libraries/System.IO.FileSystem/tests/Net5CompatTests/runtimeconfig.template.json delete mode 100644 src/libraries/System.IO/tests/Net5CompatTests/System.IO.Net5Compat.Tests.csproj delete mode 100644 src/libraries/System.IO/tests/Net5CompatTests/runtimeconfig.template.json delete mode 100644 src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.CompletionSource.Windows.cs delete mode 100644 src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Unix.cs delete mode 100644 src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Windows.cs delete mode 100644 src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.cs diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs index 77496e80d24c7..baec379eece8b 100644 --- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs +++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs @@ -312,12 +312,6 @@ private static Version GetICUVersion() version & 0xFF); } - private static readonly Lazy _net5CompatFileStream = new Lazy(() => GetStaticNonPublicBooleanPropertyValue("System.IO.Strategies.FileStreamHelpers", "UseNet5CompatStrategy")); - - public static bool IsNet5CompatFileStreamEnabled => _net5CompatFileStream.Value; - - public static bool IsNet5CompatFileStreamDisabled => !IsNet5CompatFileStreamEnabled; - private static readonly Lazy s_fileLockingDisabled = new Lazy(() => GetStaticNonPublicBooleanPropertyValue("Microsoft.Win32.SafeHandles.SafeFileHandle", "DisableFileLocking")); public static bool IsFileLockingEnabled => IsWindows || !s_fileLockingDisabled.Value; diff --git a/src/libraries/System.IO.FileSystem/System.IO.FileSystem.sln b/src/libraries/System.IO.FileSystem/System.IO.FileSystem.sln index 1958430a9e5ab..c4e10f55f1aa7 100644 --- a/src/libraries/System.IO.FileSystem/System.IO.FileSystem.sln +++ b/src/libraries/System.IO.FileSystem/System.IO.FileSystem.sln @@ -21,8 +21,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.IO.FileSystem.Disabl EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.IO.FileSystem.Manual.Tests", "tests\ManualTests\System.IO.FileSystem.Manual.Tests.csproj", "{534152EB-14A8-4EF4-B181-342A555337F1}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.IO.FileSystem.Net5Compat.Tests", "tests\Net5CompatTests\System.IO.FileSystem.Net5Compat.Tests.csproj", "{48E07F12-8597-40DE-8A37-CCBEB9D54012}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.IO.FileSystem.Tests", "tests\System.IO.FileSystem.Tests.csproj", "{3A8E16D3-8A22-4076-BB48-2CD1FBFAF81B}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Runtime.CompilerServices.Unsafe", "..\System.Runtime.CompilerServices.Unsafe\ref\System.Runtime.CompilerServices.Unsafe.csproj", "{79A74577-C550-4264-B352-51D304796B89}" diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/ReadAsync.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/ReadAsync.cs index 4e2fb6fd2046e..af30be04c8673 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileStream/ReadAsync.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/ReadAsync.cs @@ -101,7 +101,7 @@ public async Task ReadAsyncCanceledFile(int bufferSize, bool isAsync) } } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported), nameof(PlatformDetection.IsNet5CompatFileStreamDisabled))] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] [InlineData(FileShare.None, FileOptions.Asynchronous)] // FileShare.None: exclusive access [InlineData(FileShare.ReadWrite, FileOptions.Asynchronous)] // FileShare.ReadWrite: others can write to the file, the length can't be cached [InlineData(FileShare.None, FileOptions.None)] diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/SafeFileHandle.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/SafeFileHandle.cs index 0740eed02f0d8..49b31884761ab 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileStream/SafeFileHandle.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/SafeFileHandle.cs @@ -65,89 +65,5 @@ public void AccessFlushesFileClosesHandle() Assert.Equal(TestBuffer.Length, fsr.Length); } } - - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNet5CompatFileStreamEnabled))] - public async Task ThrowWhenHandlePositionIsChanged_sync() - { - await ThrowWhenHandlePositionIsChanged(useAsync: false); - } - - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported), nameof(PlatformDetection.IsNet5CompatFileStreamEnabled))] - public async Task ThrowWhenHandlePositionIsChanged_async() - { - await ThrowWhenHandlePositionIsChanged(useAsync: true); - } - - private async Task ThrowWhenHandlePositionIsChanged(bool useAsync) - { - string fileName = GetTestFilePath(); - - using (FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite, 0x100, useAsync)) - { - // write some data to move the position, flush to ensure OS position is updated - fs.Write(TestBuffer, 0, TestBuffer.Length); - fs.Flush(); - - if (fs.SafeFileHandle.IsInvalid) - { - // nothing to test - return; - } - - using (FileStream fsr = new FileStream(fs.SafeFileHandle, FileAccess.Read, TestBuffer.Length, useAsync)) - { - Assert.Equal(TestBuffer.Length, fs.Position); - Assert.Equal(TestBuffer.Length, fsr.Position); - - // Operations on original filestream will fail if data is in buffer and position changes. - - // Put data in FS write buffer and update position from FSR - fs.WriteByte(0); - fsr.Position = 0; - - if (useAsync - // Async I/O behaviors differ due to kernel-based implementation on Windows - && OperatingSystem.IsWindows() - // ReadAsync which in this case (single byte written to buffer) calls FlushAsync is now 100% async - // so it does not complete synchronously anymore - && PlatformDetection.IsNet5CompatFileStreamEnabled) - { - Assert.Throws(() => FSAssert.CompletesSynchronously(fs.ReadAsync(new byte[1], 0, 1))); - } - else - { - await Assert.ThrowsAsync(() => fs.ReadAsync(new byte[1], 0, 1)); - } - - fs.WriteByte(0); - fsr.Position++; - Assert.Throws(() => fs.Read(new byte[1], 0, 1)); - - fs.WriteByte(0); - fsr.Position++; - await Assert.ThrowsAsync(() => fs.ReadAsync(new byte[1], 0, 1)); - - fs.WriteByte(0); - fsr.Position++; - Assert.Throws(() => fs.ReadByte()); - - fs.WriteByte(0); - fsr.Position++; - Assert.Throws(() => fs.Seek(0, SeekOrigin.End)); - - fs.WriteByte(0); - fsr.Position++; - Assert.Throws(() => fs.SetLength(2)); - - fs.WriteByte(0); - fsr.Position++; - Assert.Throws(() => fs.Flush()); - - fs.WriteByte(0); - fsr.Position++; - Assert.Throws(() => fs.Dispose()); - } - } - } } } diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/WriteAsync.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/WriteAsync.cs index 06a9422589ee8..86eef052df1b4 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileStream/WriteAsync.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/WriteAsync.cs @@ -229,18 +229,6 @@ public async Task ManyConcurrentWriteAsyncs_OuterLoop( { writes[i] = WriteAsync(fs, expectedData, i * writeSize, writeSize, cancellationToken); Assert.Null(writes[i].Exception); - if (useAsync) - { - // To ensure that the buffer of a FileStream opened for async IO is flushed - // by FlushAsync in asynchronous way, we aquire a lock for every buffered WriteAsync. - // The side effect of this is that the Position of FileStream is not updated until - // the lock is released by a previous operation. - // So now all WriteAsync calls should be awaited before starting another async file operation. - if (PlatformDetection.IsNet5CompatFileStreamEnabled) - { - Assert.Equal((i + 1) * writeSize, fs.Position); - } - } } await Task.WhenAll(writes); diff --git a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/Net5CompatSwitchTests.cs b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/Net5CompatSwitchTests.cs deleted file mode 100644 index fb70c4d3c47d0..0000000000000 --- a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/Net5CompatSwitchTests.cs +++ /dev/null @@ -1,31 +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.Reflection; -using Xunit; - -namespace System.IO.Tests -{ - public class Net5CompatSwitchTests - { - [Fact] - public static void LegacySwitchIsHonored() - { - Assert.True(PlatformDetection.IsNet5CompatFileStreamEnabled); - - string filePath = Path.Combine(Path.GetTempPath(), Path.GetTempFileName()); - - using (FileStream fileStream = File.Create(filePath)) - { - object strategy = fileStream - .GetType() - .GetField("_strategy", BindingFlags.NonPublic | BindingFlags.Instance) - .GetValue(fileStream); - - Assert.Contains("Net5Compat", strategy.GetType().FullName); - } - - File.Delete(filePath); - } - } -} 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 deleted file mode 100644 index 54b9c2622937d..0000000000000 --- a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj +++ /dev/null @@ -1,53 +0,0 @@ - - - true - true - $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix - - --working-dir=/test-dir - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/runtimeconfig.template.json b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/runtimeconfig.template.json deleted file mode 100644 index 6e843517fe47a..0000000000000 --- a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/runtimeconfig.template.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "configProperties": { - "System.IO.UseNet5CompatFileStream": true - } -} diff --git a/src/libraries/System.IO/System.IO.sln b/src/libraries/System.IO/System.IO.sln index 190c3aa32bae0..e4ba7a8f57e4f 100644 --- a/src/libraries/System.IO/System.IO.sln +++ b/src/libraries/System.IO/System.IO.sln @@ -7,8 +7,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.IO", "ref\System.IO. EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.IO", "src\System.IO.csproj", "{0769544B-1A5D-4D74-94FD-899DF6C39D62}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.IO.Net5Compat.Tests", "tests\Net5CompatTests\System.IO.Net5Compat.Tests.csproj", "{0217540D-FA86-41B3-9754-7BB5096ABA3E}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.IO.Tests", "tests\System.IO.Tests.csproj", "{72923407-7B7B-44A8-BCA6-2DB562835A8F}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Runtime.CompilerServices.Unsafe", "..\System.Runtime.CompilerServices.Unsafe\ref\System.Runtime.CompilerServices.Unsafe.csproj", "{602525FF-EE47-4938-8979-32DC962109E4}" diff --git a/src/libraries/System.IO/tests/Net5CompatTests/System.IO.Net5Compat.Tests.csproj b/src/libraries/System.IO/tests/Net5CompatTests/System.IO.Net5Compat.Tests.csproj deleted file mode 100644 index c07749abe26b0..0000000000000 --- a/src/libraries/System.IO/tests/Net5CompatTests/System.IO.Net5Compat.Tests.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - System.IO - true - true - true - $(NetCoreAppCurrent) - - - - - - - - - - - - - - - - - diff --git a/src/libraries/System.IO/tests/Net5CompatTests/runtimeconfig.template.json b/src/libraries/System.IO/tests/Net5CompatTests/runtimeconfig.template.json deleted file mode 100644 index 6e843517fe47a..0000000000000 --- a/src/libraries/System.IO/tests/Net5CompatTests/runtimeconfig.template.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "configProperties": { - "System.IO.UseNet5CompatFileStream": true - } -} 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 867e13262ba6a..68f5c3af638c5 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 @@ -462,7 +462,6 @@ - @@ -1846,8 +1845,6 @@ - - @@ -2136,7 +2133,6 @@ - diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs index 5ba493ca9ca21..b519b32a05ba2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs @@ -14,9 +14,8 @@ namespace System.IO.Strategies // this type defines a set of stateless FileStream/FileStreamStrategy helper methods internal static partial class FileStreamHelpers { - // Async completion/return codes shared by: - // - AsyncWindowsFileStreamStrategy.ValueTaskSource - // - Net5CompatFileStreamStrategy.CompletionSource + // Async completion/return codes used by + // SafeFileHandle.OverlappedValueTaskSource internal static class TaskSourceCodes { internal const long NoResult = 0; @@ -338,7 +337,7 @@ internal static async Task AsyncModeCopyToAsync(SafeFileHandle handle, bool canS } } - /// Used by AsyncWindowsFileStreamStrategy and Net5CompatFileStreamStrategy CopyToAsync to enable awaiting the result of an overlapped I/O operation with minimal overhead. + /// Used by AsyncWindowsFileStreamStrategy.CopyToAsync to enable awaiting the result of an overlapped I/O operation with minimal overhead. private sealed unsafe class AsyncCopyToAwaitable : ICriticalNotifyCompletion { /// Sentinel object used to indicate that the I/O operation has completed before being awaited. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.cs index 3db2174d3fbd1..e3b419e278973 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.cs @@ -11,15 +11,9 @@ internal static partial class FileStreamHelpers /// Caches whether Serialization Guard has been disabled for file writes private static int s_cachedSerializationSwitch; - internal static bool UseNet5CompatStrategy { get; } = AppContextConfigHelper.GetBooleanConfig("System.IO.UseNet5CompatFileStream", "DOTNET_SYSTEM_IO_USENET5COMPATFILESTREAM"); - internal static FileStreamStrategy ChooseStrategy(FileStream fileStream, SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) { - // The .NET 5 Compat strategy does not support bufferSize == 0. - // To minimize the risk of introducing bugs to it, we just pass 1 to disable the buffering. - - FileStreamStrategy strategy = UseNet5CompatStrategy ? - new Net5CompatFileStreamStrategy(handle, access, bufferSize == 0 ? 1 : bufferSize, isAsync) : + FileStreamStrategy strategy = EnableBufferingIfNeeded(ChooseStrategyCore(handle, access, isAsync), bufferSize); return WrapIfDerivedType(fileStream, strategy); @@ -27,8 +21,7 @@ internal static FileStreamStrategy ChooseStrategy(FileStream fileStream, SafeFil internal static FileStreamStrategy ChooseStrategy(FileStream fileStream, string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options, long preallocationSize) { - FileStreamStrategy strategy = UseNet5CompatStrategy ? - new Net5CompatFileStreamStrategy(path, mode, access, share, bufferSize == 0 ? 1 : bufferSize, options, preallocationSize) : + FileStreamStrategy strategy = EnableBufferingIfNeeded(ChooseStrategyCore(path, mode, access, share, options, preallocationSize), bufferSize); return WrapIfDerivedType(fileStream, strategy); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.CompletionSource.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.CompletionSource.Windows.cs deleted file mode 100644 index 2c8a13feace0c..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.CompletionSource.Windows.cs +++ /dev/null @@ -1,250 +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.Buffers; -using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Threading; -using System.Threading.Tasks; -using TaskSourceCodes = System.IO.Strategies.FileStreamHelpers.TaskSourceCodes; - -namespace System.IO.Strategies -{ - internal sealed partial class Net5CompatFileStreamStrategy : FileStreamStrategy - { - // This is an internal object extending TaskCompletionSource with fields - // for all of the relevant data necessary to complete the IO operation. - // This is used by IOCallback and all of the async methods. - private unsafe class CompletionSource : TaskCompletionSource - { - internal static readonly unsafe IOCompletionCallback s_ioCallback = IOCallback; - - private static Action? s_cancelCallback; - - private readonly Net5CompatFileStreamStrategy _strategy; - private readonly int _numBufferedBytes; - private CancellationTokenRegistration _cancellationRegistration; -#if DEBUG - private bool _cancellationHasBeenRegistered; -#endif - private NativeOverlapped* _overlapped; // Overlapped class responsible for operations in progress when an appdomain unload occurs - private long _result; // Using long since this needs to be used in Interlocked APIs - - // Using RunContinuationsAsynchronously for compat reasons (old API used Task.Factory.StartNew for continuations) - internal CompletionSource(Net5CompatFileStreamStrategy strategy, PreAllocatedOverlapped? preallocatedOverlapped, - int numBufferedBytes, byte[]? bytes) : base(TaskCreationOptions.RunContinuationsAsynchronously) - { - _numBufferedBytes = numBufferedBytes; - _strategy = strategy; - _result = TaskSourceCodes.NoResult; - - // The _preallocatedOverlapped is null if the internal buffer was never created, so we check for - // a non-null bytes before using the stream's _preallocatedOverlapped - _overlapped = bytes != null && strategy.CompareExchangeCurrentOverlappedOwner(this, null) == null ? - strategy._fileHandle.ThreadPoolBinding!.AllocateNativeOverlapped(preallocatedOverlapped!) : // allocated when buffer was created, and buffer is non-null - strategy._fileHandle.ThreadPoolBinding!.AllocateNativeOverlapped(s_ioCallback, this, bytes); - Debug.Assert(_overlapped != null, "AllocateNativeOverlapped returned null"); - } - - internal NativeOverlapped* Overlapped => _overlapped; - - public void SetCompletedSynchronously(int numBytes) - { - ReleaseNativeResource(); - TrySetResult(numBytes + _numBufferedBytes); - } - - public void RegisterForCancellation(CancellationToken cancellationToken) - { -#if DEBUG - Debug.Assert(cancellationToken.CanBeCanceled); - Debug.Assert(!_cancellationHasBeenRegistered, "Cannot register for cancellation twice"); - _cancellationHasBeenRegistered = true; -#endif - - // Quick check to make sure the IO hasn't completed - if (_overlapped != null) - { - Action? cancelCallback = s_cancelCallback ??= Cancel; - - // Register the cancellation only if the IO hasn't completed - long packedResult = Interlocked.CompareExchange(ref _result, TaskSourceCodes.RegisteringCancellation, TaskSourceCodes.NoResult); - if (packedResult == TaskSourceCodes.NoResult) - { - _cancellationRegistration = cancellationToken.UnsafeRegister(cancelCallback, this); - - // Switch the result, just in case IO completed while we were setting the registration - packedResult = Interlocked.Exchange(ref _result, TaskSourceCodes.NoResult); - } - else if (packedResult != TaskSourceCodes.CompletedCallback) - { - // Failed to set the result, IO is in the process of completing - // Attempt to take the packed result - packedResult = Interlocked.Exchange(ref _result, TaskSourceCodes.NoResult); - } - - // If we have a callback that needs to be completed - if ((packedResult != TaskSourceCodes.NoResult) && (packedResult != TaskSourceCodes.CompletedCallback) && (packedResult != TaskSourceCodes.RegisteringCancellation)) - { - CompleteCallback((ulong)packedResult); - } - } - } - - internal virtual void ReleaseNativeResource() - { - // Ensure that cancellation has been completed and cleaned up. - _cancellationRegistration.Dispose(); - - // Free the overlapped. - // NOTE: The cancellation must *NOT* be running at this point, or it may observe freed memory - // (this is why we disposed the registration above). - if (_overlapped != null) - { - _strategy._fileHandle.ThreadPoolBinding!.FreeNativeOverlapped(_overlapped); - _overlapped = null; - } - - // Ensure we're no longer set as the current completion source (we may not have been to begin with). - // Only one operation at a time is eligible to use the preallocated overlapped, - _strategy.CompareExchangeCurrentOverlappedOwner(null, this); - } - - // When doing IO asynchronously (i.e. _isAsync==true), this callback is - // called by a free thread in the threadpool when the IO operation - // completes. - internal static void IOCallback(uint errorCode, uint numBytes, NativeOverlapped* pOverlapped) - { - // Extract the completion source from the overlapped. The state in the overlapped - // will either be a FileStreamStrategy (in the case where the preallocated overlapped was used), - // in which case the operation being completed is its _currentOverlappedOwner, or it'll - // be directly the FileStreamCompletionSource that's completing (in the case where the preallocated - // overlapped was already in use by another operation). - object? state = ThreadPoolBoundHandle.GetNativeOverlappedState(pOverlapped); - Debug.Assert(state is Net5CompatFileStreamStrategy || state is CompletionSource); - CompletionSource completionSource = state switch - { - Net5CompatFileStreamStrategy strategy => strategy._currentOverlappedOwner!, // must be owned - _ => (CompletionSource)state - }; - Debug.Assert(completionSource != null); - Debug.Assert(completionSource._overlapped == pOverlapped, "Overlaps don't match"); - - // Handle reading from & writing to closed pipes. While I'm not sure - // this is entirely necessary anymore, maybe it's possible for - // an async read on a pipe to be issued and then the pipe is closed, - // returning this error. This may very well be necessary. - ulong packedResult; - if (errorCode != 0 && errorCode != Interop.Errors.ERROR_BROKEN_PIPE && errorCode != Interop.Errors.ERROR_NO_DATA) - { - packedResult = ((ulong)TaskSourceCodes.ResultError | errorCode); - } - else - { - packedResult = ((ulong)TaskSourceCodes.ResultSuccess | numBytes); - } - - // Stow the result so that other threads can observe it - // And, if no other thread is registering cancellation, continue - if (TaskSourceCodes.NoResult == Interlocked.Exchange(ref completionSource._result, (long)packedResult)) - { - // Successfully set the state, attempt to take back the callback - if (Interlocked.Exchange(ref completionSource._result, TaskSourceCodes.CompletedCallback) != TaskSourceCodes.NoResult) - { - // Successfully got the callback, finish the callback - completionSource.CompleteCallback(packedResult); - } - // else: Some other thread stole the result, so now it is responsible to finish the callback - } - // else: Some other thread is registering a cancellation, so it *must* finish the callback - } - - private void CompleteCallback(ulong packedResult) - { - // Free up the native resource and cancellation registration - CancellationToken cancellationToken = _cancellationRegistration.Token; // access before disposing registration - ReleaseNativeResource(); - - // Unpack the result and send it to the user - long result = (long)(packedResult & TaskSourceCodes.ResultMask); - if (result == TaskSourceCodes.ResultError) - { - int errorCode = unchecked((int)(packedResult & uint.MaxValue)); - if (errorCode == Interop.Errors.ERROR_OPERATION_ABORTED) - { - TrySetCanceled(cancellationToken.IsCancellationRequested ? cancellationToken : new CancellationToken(true)); - } - else - { - Exception e = Win32Marshal.GetExceptionForWin32Error(errorCode); - e.SetCurrentStackTrace(); - TrySetException(e); - } - } - else - { - Debug.Assert(result == TaskSourceCodes.ResultSuccess, "Unknown result"); - TrySetResult((int)(packedResult & uint.MaxValue) + _numBufferedBytes); - } - } - - private static void Cancel(object? state) - { - // WARNING: This may potentially be called under a lock (during cancellation registration) - - Debug.Assert(state is CompletionSource, "Unknown state passed to cancellation"); - CompletionSource completionSource = (CompletionSource)state; - Debug.Assert(completionSource._overlapped != null && !completionSource.Task.IsCompleted, "IO should not have completed yet"); - - // If the handle is still valid, attempt to cancel the IO - if (!completionSource._strategy._fileHandle.IsInvalid && - !Interop.Kernel32.CancelIoEx(completionSource._strategy._fileHandle, completionSource._overlapped)) - { - int errorCode = Marshal.GetLastPInvokeError(); - - // ERROR_NOT_FOUND is returned if CancelIoEx cannot find the request to cancel. - // This probably means that the IO operation has completed. - if (errorCode != Interop.Errors.ERROR_NOT_FOUND) - { - throw Win32Marshal.GetExceptionForWin32Error(errorCode); - } - } - } - - public static CompletionSource Create(Net5CompatFileStreamStrategy strategy, PreAllocatedOverlapped? preallocatedOverlapped, - int numBufferedBytesRead, ReadOnlyMemory memory) - { - // If the memory passed in is the strategy's internal buffer, we can use the base FileStreamCompletionSource, - // which has a PreAllocatedOverlapped with the memory already pinned. Otherwise, we use the derived - // MemoryFileStreamCompletionSource, which Retains the memory, which will result in less pinning in the case - // where the underlying memory is backed by pre-pinned buffers. - return preallocatedOverlapped != null && MemoryMarshal.TryGetArray(memory, out ArraySegment buffer) - && preallocatedOverlapped.IsUserObject(buffer.Array) // preallocatedOverlapped is allocated when BufferedStream|Net5CompatFileStreamStrategy allocates the buffer - ? new CompletionSource(strategy, preallocatedOverlapped, numBufferedBytesRead, buffer.Array) - : new MemoryFileStreamCompletionSource(strategy, numBufferedBytesRead, memory); - } - } - - /// - /// Extends with to support disposing of a - /// when the operation has completed. This should only be used - /// when memory doesn't wrap a byte[]. - /// - private sealed class MemoryFileStreamCompletionSource : CompletionSource - { - private MemoryHandle _handle; // mutable struct; do not make this readonly - - internal MemoryFileStreamCompletionSource(Net5CompatFileStreamStrategy strategy, int numBufferedBytes, ReadOnlyMemory memory) - : base(strategy, null, numBufferedBytes, null) // this type handles the pinning, so null is passed for bytes - { - _handle = memory.Pin(); - } - - internal override void ReleaseNativeResource() - { - _handle.Dispose(); - base.ReleaseNativeResource(); - } - } - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Unix.cs deleted file mode 100644 index 2feffb5cb6be5..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Unix.cs +++ /dev/null @@ -1,601 +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 Microsoft.Win32.SafeHandles; -using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Runtime.Versioning; -using System.Threading; -using System.Threading.Tasks; - -namespace System.IO.Strategies -{ - /// Provides an implementation of a file stream for Unix files. - internal sealed partial class Net5CompatFileStreamStrategy : FileStreamStrategy - { - /// Advanced options requested when opening the file. - private FileOptions _options; - - /// If the file was opened with FileMode.Append, the length of the file when opened; otherwise, -1. - private long _appendStart = -1; - - /// - /// Extra state used by the file stream when _useAsyncIO is true. This includes - /// the semaphore used to serialize all operation, the buffer/offset/count provided by the - /// caller for ReadAsync/WriteAsync operations, and the last successful task returned - /// synchronously from ReadAsync which can be reused if the count matches the next request. - /// Only initialized when is true. - /// - private AsyncState? _asyncState; - - private void Init(FileMode mode, string originalPath, FileOptions options) - { - // FileStream performs most of the general argument validation. We can assume here that the arguments - // are all checked and consistent (e.g. non-null-or-empty path; valid enums in mode, access, share, and options; etc.) - // Store the arguments - _options = options; - - if (_useAsyncIO) - { - _asyncState = new AsyncState(); - } - - if (mode == FileMode.Append) - { - // Jump to the end of the file if opened as Append. - _appendStart = SeekCore(_fileHandle, 0, SeekOrigin.End); - } - - Debug.Assert(_fileHandle.IsAsync == _useAsyncIO); - } - - /// Initializes a stream from an already open file handle (file descriptor). - private void InitFromHandle(SafeFileHandle handle, FileAccess access, bool useAsyncIO) - { - if (useAsyncIO) - _asyncState = new AsyncState(); - - if (handle.CanSeek) - SeekCore(handle, 0, SeekOrigin.Current); - } - - public override bool CanSeek => _fileHandle.CanSeek; - - public override long Length - { - get - { - // Get the length of the file as reported by the OS - long length = RandomAccess.GetFileLength(_fileHandle); - - // But we may have buffered some data to be written that puts our length - // beyond what the OS is aware of. Update accordingly. - if (_writePos > 0 && _filePosition + _writePos > length) - { - length = _writePos + _filePosition; - } - - return length; - } - } - - /// Prevents other processes from reading from or writing to the FileStream. - /// The beginning of the range to lock. - /// The range to be locked. - internal override void Lock(long position, long length) => - FileStreamHelpers.Lock(_fileHandle, CanWrite, position, length); - - /// Allows access by other processes to all or part of a file that was previously locked. - /// The beginning of the range to unlock. - /// The range to be unlocked. - internal override void Unlock(long position, long length) => - FileStreamHelpers.Unlock(_fileHandle, position, length); - - /// Releases the unmanaged resources used by the stream. - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected override void Dispose(bool disposing) - { - try - { - if (_fileHandle != null && !_fileHandle.IsClosed) - { - // Flush any remaining data in the file - try - { - FlushWriteBuffer(); - } - catch (Exception e) when (!disposing && FileStreamHelpers.IsIoRelatedException(e)) - { - // On finalization, ignore failures from trying to flush the write buffer, - // e.g. if this stream is wrapping a pipe and the pipe is now broken. - } - - // Closing the file handle can fail, e.g. due to out of disk space - // Throw these errors as exceptions when disposing - if (_fileHandle != null && !_fileHandle.IsClosed && disposing) - { - SafeFileHandle.t_lastCloseErrorInfo = null; - - _fileHandle.Dispose(); - - if (SafeFileHandle.t_lastCloseErrorInfo != null) - { - throw Interop.GetExceptionForIoErrno(SafeFileHandle.t_lastCloseErrorInfo.GetValueOrDefault(), _fileHandle.Path, isDirectory: false); - } - } - } - } - finally - { - if (_fileHandle != null && !_fileHandle.IsClosed) - { - _fileHandle.Dispose(); - } - base.Dispose(disposing); - } - } - - public override ValueTask DisposeAsync() - { - // On Unix, we don't have any special support for async I/O, simply queueing writes - // rather than doing them synchronously. As such, if we're "using async I/O" and we - // have something to flush, queue the call to Dispose, so that we end up queueing whatever - // write work happens to flush the buffer. Otherwise, just delegate to the base implementation, - // which will synchronously invoke Dispose. We don't need to factor in the current type - // as we're using the virtual Dispose either way, and therefore factoring in whatever - // override may already exist on a derived type. - if (_useAsyncIO && _writePos > 0) - { - return new ValueTask(Task.Factory.StartNew(static s => ((Net5CompatFileStreamStrategy)s!).Dispose(), this, - CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default)); - } - - return base.DisposeAsync(); - } - - private void FlushWriteBufferForWriteByte() - { -#pragma warning disable CA1416 // Validate platform compatibility, issue: https://github.com/dotnet/runtime/issues/44542 - _asyncState?.Wait(); -#pragma warning restore CA1416 - try { FlushWriteBuffer(); } - finally { _asyncState?.Release(); } - } - - /// Writes any data in the write buffer to the underlying stream and resets the buffer. - private void FlushWriteBuffer(bool calledFromFinalizer = false) - { - AssertBufferInvariants(); - if (_writePos > 0) - { - WriteNative(new ReadOnlySpan(GetBuffer(), 0, _writePos)); - _writePos = 0; - } - } - - /// Sets the length of this stream to the given value. - /// The new length of the stream. - public override void SetLength(long value) - { - FlushInternalBuffer(); - - if (_appendStart != -1 && value < _appendStart) - { - throw new IOException(SR.IO_SetLengthAppendTruncate); - } - - VerifyOSHandlePosition(); - - CheckFileCall(Interop.Sys.FTruncate(_fileHandle, value)); - - // Set file pointer to end of file - if (_filePosition > value) - { - SeekCore(_fileHandle, 0, SeekOrigin.End); - } - } - - /// Reads a block of bytes from the stream and writes the data in a given buffer. - private int ReadSpan(Span destination) - { - PrepareForReading(); - - // Are there any bytes available in the read buffer? If yes, - // we can just return from the buffer. If the buffer is empty - // or has no more available data in it, we can either refill it - // (and then read from the buffer into the user's buffer) or - // we can just go directly into the user's buffer, if they asked - // for more data than we'd otherwise buffer. - int numBytesAvailable = _readLength - _readPos; - bool readFromOS = false; - if (numBytesAvailable == 0) - { - // If we're not able to seek, then we're not able to rewind the stream (i.e. flushing - // a read buffer), in which case we don't want to use a read buffer. Similarly, if - // the user has asked for more data than we can buffer, we also want to skip the buffer. - if (!CanSeek || (destination.Length >= _bufferLength)) - { - // Read directly into the user's buffer - _readPos = _readLength = 0; - return ReadNative(destination); - } - else - { - // Read into our buffer. - _readLength = numBytesAvailable = ReadNative(GetBuffer()); - _readPos = 0; - if (numBytesAvailable == 0) - { - return 0; - } - - // Note that we did an OS read as part of this Read, so that later - // we don't try to do one again if what's in the buffer doesn't - // meet the user's request. - readFromOS = true; - } - } - - // Now that we know there's data in the buffer, read from it into the user's buffer. - Debug.Assert(numBytesAvailable > 0, "Data must be in the buffer to be here"); - int bytesRead = Math.Min(numBytesAvailable, destination.Length); - new Span(GetBuffer(), _readPos, bytesRead).CopyTo(destination); - _readPos += bytesRead; - - // We may not have had enough data in the buffer to completely satisfy the user's request. - // While Read doesn't require that we return as much data as the user requested (any amount - // up to the requested count is fine), FileStream on Windows tries to do so by doing a - // subsequent read from the file if we tried to satisfy the request with what was in the - // buffer but the buffer contained less than the requested count. To be consistent with that - // behavior, we do the same thing here on Unix. Note that we may still get less the requested - // amount, as the OS may give us back fewer than we request, either due to reaching the end of - // file, or due to its own whims. - if (!readFromOS && bytesRead < destination.Length) - { - Debug.Assert(_readPos == _readLength, "bytesToRead should only be < destination.Length if numBytesAvailable < destination.Length"); - _readPos = _readLength = 0; // no data left in the read buffer - bytesRead += ReadNative(destination.Slice(bytesRead)); - } - - return bytesRead; - } - - /// Unbuffered, reads a block of bytes from the file handle into the given buffer. - /// The buffer into which data from the file is read. - /// - /// The total number of bytes read into the buffer. This might be less than the number of bytes requested - /// if that number of bytes are not currently available, or zero if the end of the stream is reached. - /// - private unsafe int ReadNative(Span buffer) - { - FlushWriteBuffer(); // we're about to read; dump the write buffer - - VerifyOSHandlePosition(); - - int bytesRead; - fixed (byte* bufPtr = &MemoryMarshal.GetReference(buffer)) - { - bytesRead = CheckFileCall(Interop.Sys.Read(_fileHandle, bufPtr, buffer.Length)); - Debug.Assert(bytesRead <= buffer.Length); - } - _filePosition += bytesRead; - return bytesRead; - } - - /// - /// Asynchronously reads a sequence of bytes from the current stream and advances - /// the position within the stream by the number of bytes read. - /// - /// The buffer to write the data into. - /// The token to monitor for cancellation requests. - /// If the operation completes synchronously, the number of bytes read. - /// A task that represents the asynchronous read operation. - private Task? ReadAsyncInternal(Memory destination, CancellationToken cancellationToken, out int synchronousResult) - { - Debug.Assert(_useAsyncIO); - Debug.Assert(_asyncState != null); - - if (!CanRead) // match Windows behavior; this gets thrown synchronously - { - ThrowHelper.ThrowNotSupportedException_UnreadableStream(); - } - - // Serialize operations using the semaphore. - Task waitTask = _asyncState.WaitAsync(); - - // If we got ownership immediately, and if there's enough data in our buffer - // to satisfy the full request of the caller, hand back the buffered data. - // While it would be a legal implementation of the Read contract, we don't - // hand back here less than the amount requested so as to match the behavior - // in ReadCore that will make a native call to try to fulfill the remainder - // of the request. - if (waitTask.Status == TaskStatus.RanToCompletion) - { - int numBytesAvailable = _readLength - _readPos; - if (numBytesAvailable >= destination.Length) - { - try - { - PrepareForReading(); - - new Span(GetBuffer(), _readPos, destination.Length).CopyTo(destination.Span); - _readPos += destination.Length; - - synchronousResult = destination.Length; - return null; - } - catch (Exception exc) - { - synchronousResult = 0; - return Task.FromException(exc); - } - finally - { - _asyncState.Release(); - } - } - } - - // Otherwise, issue the whole request asynchronously. - synchronousResult = 0; - _asyncState.Memory = destination; - return waitTask.ContinueWith(static (t, s) => - { - // The options available on Unix for writing asynchronously to an arbitrary file - // handle typically amount to just using another thread to do the synchronous write, - // which is exactly what this implementation does. This does mean there are subtle - // differences in certain FileStream behaviors between Windows and Unix when multiple - // asynchronous operations are issued against the stream to execute concurrently; on - // Unix the operations will be serialized due to the usage of a semaphore, but the - // position /length information won't be updated until after the write has completed, - // whereas on Windows it may happen before the write has completed. - - Debug.Assert(t.Status == TaskStatus.RanToCompletion); - var thisRef = (Net5CompatFileStreamStrategy)s!; - Debug.Assert(thisRef._asyncState != null); - try - { - Memory memory = thisRef._asyncState.Memory; - thisRef._asyncState.Memory = default; - return thisRef.ReadSpan(memory.Span); - } - finally { thisRef._asyncState.Release(); } - }, this, CancellationToken.None, TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default); - } - - /// Reads from the file handle into the buffer, overwriting anything in it. - private int FillReadBufferForReadByte() - { -#pragma warning disable CA1416 // Validate platform compatibility, issue: https://github.com/dotnet/runtime/issues/44542 - _asyncState?.Wait(); -#pragma warning restore CA1416 - try { return ReadNative(_buffer); } - finally { _asyncState?.Release(); } - } - - /// Writes a block of bytes to the file stream. - /// The buffer containing data to write to the stream. - private void WriteSpan(ReadOnlySpan source) - { - PrepareForWriting(); - - // If no data is being written, nothing more to do. - if (source.Length == 0) - { - return; - } - - // If there's already data in our write buffer, then we need to go through - // our buffer to ensure data isn't corrupted. - if (_writePos > 0) - { - // If there's space remaining in the buffer, then copy as much as - // we can from the user's buffer into ours. - int spaceRemaining = _bufferLength - _writePos; - if (spaceRemaining >= source.Length) - { - source.CopyTo(GetBuffer().AsSpan(_writePos)); - _writePos += source.Length; - return; - } - else if (spaceRemaining > 0) - { - source.Slice(0, spaceRemaining).CopyTo(GetBuffer().AsSpan(_writePos)); - _writePos += spaceRemaining; - source = source.Slice(spaceRemaining); - } - - // At this point, the buffer is full, so flush it out. - FlushWriteBuffer(); - } - - // Our buffer is now empty. If using the buffer would slow things down (because - // the user's looking to write more data than we can store in the buffer), - // skip the buffer. Otherwise, put the remaining data into the buffer. - Debug.Assert(_writePos == 0); - if (source.Length >= _bufferLength) - { - WriteNative(source); - } - else - { - source.CopyTo(new Span(GetBuffer())); - _writePos = source.Length; - } - } - - /// Unbuffered, writes a block of bytes to the file stream. - /// The buffer containing data to write to the stream. - private unsafe void WriteNative(ReadOnlySpan source) - { - VerifyOSHandlePosition(); - - fixed (byte* bufPtr = &MemoryMarshal.GetReference(source)) - { - int offset = 0; - int count = source.Length; - while (count > 0) - { - int bytesWritten = CheckFileCall(Interop.Sys.Write(_fileHandle, bufPtr + offset, count)); - _filePosition += bytesWritten; - offset += bytesWritten; - count -= bytesWritten; - } - } - } - - /// - /// Asynchronously writes a sequence of bytes to the current stream, advances - /// the current position within this stream by the number of bytes written, and - /// monitors cancellation requests. - /// - /// The buffer to write data from. - /// The token to monitor for cancellation requests. - /// A task that represents the asynchronous write operation. - private ValueTask WriteAsyncInternal(ReadOnlyMemory source, CancellationToken cancellationToken) - { - Debug.Assert(_useAsyncIO); - Debug.Assert(_asyncState != null); - - if (cancellationToken.IsCancellationRequested) - return ValueTask.FromCanceled(cancellationToken); - - if (_fileHandle.IsClosed) - ThrowHelper.ThrowObjectDisposedException_FileClosed(); - - if (!CanWrite) // match Windows behavior; this gets thrown synchronously - { - ThrowHelper.ThrowNotSupportedException_UnwritableStream(); - } - - // Serialize operations using the semaphore. - Task waitTask = _asyncState.WaitAsync(cancellationToken); - - // If we got ownership immediately, and if there's enough space in our buffer - // to buffer the entire write request, then do so and we're done. - if (waitTask.Status == TaskStatus.RanToCompletion) - { - int spaceRemaining = _bufferLength - _writePos; - if (spaceRemaining >= source.Length) - { - try - { - PrepareForWriting(); - - source.Span.CopyTo(new Span(GetBuffer(), _writePos, source.Length)); - _writePos += source.Length; - - return default; - } - catch (Exception exc) - { - return ValueTask.FromException(exc); - } - finally - { - _asyncState.Release(); - } - } - } - - // Otherwise, issue the whole request asynchronously. - _asyncState.ReadOnlyMemory = source; - return new ValueTask(waitTask.ContinueWith(static (t, s) => - { - // The options available on Unix for writing asynchronously to an arbitrary file - // handle typically amount to just using another thread to do the synchronous write, - // which is exactly what this implementation does. This does mean there are subtle - // differences in certain FileStream behaviors between Windows and Unix when multiple - // asynchronous operations are issued against the stream to execute concurrently; on - // Unix the operations will be serialized due to the usage of a semaphore, but the - // position/length information won't be updated until after the write has completed, - // whereas on Windows it may happen before the write has completed. - - Debug.Assert(t.Status == TaskStatus.RanToCompletion); - var thisRef = (Net5CompatFileStreamStrategy)s!; - Debug.Assert(thisRef._asyncState != null); - try - { - ReadOnlyMemory readOnlyMemory = thisRef._asyncState.ReadOnlyMemory; - thisRef._asyncState.ReadOnlyMemory = default; - thisRef.WriteSpan(readOnlyMemory.Span); - } - finally { thisRef._asyncState.Release(); } - }, this, CancellationToken.None, TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default)); - } - - /// Sets the current position of this stream to the given value. - /// The point relative to origin from which to begin seeking. - /// - /// Specifies the beginning, the end, or the current position as a reference - /// point for offset, using a value of type SeekOrigin. - /// - /// The new position in the stream. - public override long Seek(long offset, SeekOrigin origin) - { - if (origin < SeekOrigin.Begin || origin > SeekOrigin.End) - { - throw new ArgumentException(SR.Argument_InvalidSeekOrigin, nameof(origin)); - } - if (_fileHandle.IsClosed) - { - ThrowHelper.ThrowObjectDisposedException_FileClosed(); - } - if (!CanSeek) - { - ThrowHelper.ThrowNotSupportedException_UnseekableStream(); - } - - VerifyOSHandlePosition(); - - // Flush our write/read buffer. FlushWrite will output any write buffer we have and reset _bufferWritePos. - // We don't call FlushRead, as that will do an unnecessary seek to rewind the read buffer, and since we're - // about to seek and update our position, we can simply update the offset as necessary and reset our read - // position and length to 0. (In the future, for some simple cases we could potentially add an optimization - // here to just move data around in the buffer for short jumps, to avoid re-reading the data from disk.) - FlushWriteBuffer(); - if (origin == SeekOrigin.Current) - { - offset -= (_readLength - _readPos); - } - _readPos = _readLength = 0; - - // Keep track of where we were, in case we're in append mode and need to verify - long oldPos = 0; - if (_appendStart >= 0) - { - oldPos = SeekCore(_fileHandle, 0, SeekOrigin.Current); - } - - // Jump to the new location - long pos = SeekCore(_fileHandle, offset, origin); - - // Prevent users from overwriting data in a file that was opened in append mode. - if (_appendStart != -1 && pos < _appendStart) - { - SeekCore(_fileHandle, oldPos, SeekOrigin.Begin); - throw new IOException(SR.IO_SeekAppendOverwrite); - } - - // Return the new position - return pos; - } - - private int CheckFileCall(int result, bool ignoreNotSupported = false) - { - FileStreamHelpers.CheckFileCall(result, _fileHandle?.Path, ignoreNotSupported); - - return result; - } - - /// State used when the stream is in async mode. - private sealed class AsyncState : SemaphoreSlim - { - internal ReadOnlyMemory ReadOnlyMemory; - internal Memory Memory; - - /// Initialize the AsyncState. - internal AsyncState() : base(initialCount: 1, maxCount: 1) { } - } - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Windows.cs deleted file mode 100644 index a2e7666114524..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Windows.cs +++ /dev/null @@ -1,1104 +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.Diagnostics; -using System.Runtime.InteropServices; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Win32.SafeHandles; - -/* - * Win32FileStream supports different modes of accessing the disk - async mode - * and sync mode. They are two completely different codepaths in the - * sync & async methods (i.e. Read/Write vs. ReadAsync/WriteAsync). File - * handles in NT can be opened in only sync or overlapped (async) mode, - * and we have to deal with this pain. Stream has implementations of - * the sync methods in terms of the async ones, so we'll - * call through to our base class to get those methods when necessary. - * - * Also buffering is added into Win32FileStream as well. Folded in the - * code from BufferedStream, so all the comments about it being mostly - * aggressive (and the possible perf improvement) apply to Win32FileStream as - * well. Also added some buffering to the async code paths. - * - * Class Invariants: - * The class has one buffer, shared for reading & writing. It can only be - * used for one or the other at any point in time - not both. The following - * should be true: - * 0 <= _readPos <= _readLen < _bufferSize - * 0 <= _writePos < _bufferSize - * _readPos == _readLen && _readPos > 0 implies the read buffer is valid, - * but we're at the end of the buffer. - * _readPos == _readLen == 0 means the read buffer contains garbage. - * Either _writePos can be greater than 0, or _readLen & _readPos can be - * greater than zero, but neither can be greater than zero at the same time. - * - */ - -namespace System.IO.Strategies -{ - internal sealed partial class Net5CompatFileStreamStrategy : FileStreamStrategy - { - private long _appendStart; // When appending, prevent overwriting file. - - private Task _activeBufferOperation = Task.CompletedTask; // tracks in-progress async ops using the buffer - private PreAllocatedOverlapped? _preallocatedOverlapped; // optimization for async ops to avoid per-op allocations - private CompletionSource? _currentOverlappedOwner; // async op currently using the preallocated overlapped - - private void Init(FileMode mode, string originalPath, FileOptions options) - { - Debug.Assert(!_useAsyncIO || _fileHandle.ThreadPoolBinding != null); - - // For Append mode... - if (mode == FileMode.Append) - { - _appendStart = SeekCore(_fileHandle, 0, SeekOrigin.End); - } - else - { - _appendStart = -1; - } - } - - private void InitFromHandle(SafeFileHandle handle, FileAccess access, bool useAsyncIO) - { -#if DEBUG - bool hadBinding = handle.ThreadPoolBinding != null; - - try - { -#endif - InitFromHandleImpl(handle, useAsyncIO); -#if DEBUG - } - catch - { - Debug.Assert(hadBinding || handle.ThreadPoolBinding == null, "We should never error out with a ThreadPoolBinding we've added"); - throw; - } -#endif - } - - private void InitFromHandleImpl(SafeFileHandle handle, bool useAsyncIO) - { - handle.EnsureThreadPoolBindingInitialized(); - - if (handle.CanSeek) - SeekCore(handle, 0, SeekOrigin.Current); - else - _filePosition = 0; - } - - private bool HasActiveBufferOperation => !_activeBufferOperation.IsCompleted; - - public override bool CanSeek => _fileHandle.CanSeek; - - public unsafe override long Length - { - get - { - long len = RandomAccess.GetFileLength(_fileHandle); - - // If we're writing near the end of the file, we must include our - // internal buffer in our Length calculation. Don't flush because - // we use the length of the file in our async write method. - if (_writePos > 0 && _filePosition + _writePos > len) - len = _writePos + _filePosition; - - return len; - } - } - - protected override void Dispose(bool disposing) - { - // Nothing will be done differently based on whether we are - // disposing vs. finalizing. This is taking advantage of the - // weak ordering between normal finalizable objects & critical - // finalizable objects, which I included in the SafeHandle - // design for Win32FileStream, which would often "just work" when - // finalized. - try - { - if (_fileHandle != null && !_fileHandle.IsClosed && _writePos > 0) - { - // Flush data to disk iff we were writing. After - // thinking about this, we also don't need to flush - // our read position, regardless of whether the handle - // was exposed to the user. They probably would NOT - // want us to do this. - try - { - FlushWriteBuffer(!disposing); - } - catch (Exception e) when (!disposing && FileStreamHelpers.IsIoRelatedException(e)) - { - // On finalization, ignore failures from trying to flush the write buffer, - // e.g. if this stream is wrapping a pipe and the pipe is now broken. - } - } - } - finally - { - if (_fileHandle != null && !_fileHandle.IsClosed) - { - _fileHandle.ThreadPoolBinding?.Dispose(); - _fileHandle.Dispose(); - } - - _preallocatedOverlapped?.Dispose(); - - // Don't set the buffer to null, to avoid a NullReferenceException - // when users have a race condition in their code (i.e. they call - // Close when calling another method on Stream like Read). - } - } - - public override async ValueTask DisposeAsync() - { - // Same logic as in Dispose(), except with async counterparts. - // TODO: https://github.com/dotnet/runtime/issues/27643: FlushAsync does synchronous work. - try - { - if (_fileHandle != null && !_fileHandle.IsClosed && _writePos > 0) - { - await FlushAsync(default).ConfigureAwait(false); - } - } - finally - { - if (_fileHandle != null && !_fileHandle.IsClosed) - { - _fileHandle.ThreadPoolBinding?.Dispose(); - _fileHandle.Dispose(); - } - - _preallocatedOverlapped?.Dispose(); - GC.SuppressFinalize(this); // the handle is closed; nothing further for the finalizer to do - } - } - - // Returns a task that flushes the internal write buffer - private Task FlushWriteAsync(CancellationToken cancellationToken) - { - Debug.Assert(_useAsyncIO); - Debug.Assert(_readPos == 0 && _readLength == 0, "FileStream: Read buffer must be empty in FlushWriteAsync!"); - - // If the buffer is already flushed, don't spin up the OS write - if (_writePos == 0) return Task.CompletedTask; - - Task flushTask = WriteAsyncInternalCore(new ReadOnlyMemory(GetBuffer(), 0, _writePos), cancellationToken); - _writePos = 0; - - // Update the active buffer operation - _activeBufferOperation = HasActiveBufferOperation ? - Task.WhenAll(_activeBufferOperation, flushTask) : - flushTask; - - return flushTask; - } - - private void FlushWriteBufferForWriteByte() => FlushWriteBuffer(); - - // Writes are buffered. Anytime the buffer fills up - // (_writePos + delta > _bufferSize) or the buffer switches to reading - // and there is left over data (_writePos > 0), this function must be called. - private void FlushWriteBuffer(bool calledFromFinalizer = false) - { - if (_writePos == 0) return; - Debug.Assert(_readPos == 0 && _readLength == 0, "FileStream: Read buffer must be empty in FlushWrite!"); - - if (_useAsyncIO) - { - Task writeTask = FlushWriteAsync(CancellationToken.None); - // With our Whidbey async IO & overlapped support for AD unloads, - // we don't strictly need to block here to release resources - // since that support takes care of the pinning & freeing the - // overlapped struct. We need to do this when called from - // Close so that the handle is closed when Close returns, but - // we don't need to call EndWrite from the finalizer. - // Additionally, if we do call EndWrite, we block forever - // because AD unloads prevent us from running the managed - // callback from the IO completion port. Blocking here when - // called from the finalizer during AD unload is clearly wrong, - // but we can't use any sort of test for whether the AD is - // unloading because if we weren't unloading, an AD unload - // could happen on a separate thread before we call EndWrite. - if (!calledFromFinalizer) - { - writeTask.GetAwaiter().GetResult(); - } - } - else - { - WriteCore(new ReadOnlySpan(GetBuffer(), 0, _writePos)); - } - - _writePos = 0; - } - - public override void SetLength(long value) - { - // Handle buffering updates. - if (_writePos > 0) - { - FlushWriteBuffer(); - } - else if (_readPos < _readLength) - { - FlushReadBuffer(); - } - _readPos = 0; - _readLength = 0; - - if (_appendStart != -1 && value < _appendStart) - throw new IOException(SR.IO_SetLengthAppendTruncate); - SetLengthCore(value); - } - - // We absolutely need this method broken out so that WriteInternalCoreAsync can call - // a method without having to go through buffering code that might call FlushWrite. - private unsafe void SetLengthCore(long value) - { - Debug.Assert(value >= 0, "value >= 0"); - VerifyOSHandlePosition(); - - FileStreamHelpers.SetFileLength(_fileHandle, value); - - if (_filePosition > value) - { - SeekCore(_fileHandle, 0, SeekOrigin.End); - } - } - - private int ReadSpan(Span destination) - { - Debug.Assert(!_useAsyncIO, "Must only be used when in synchronous mode"); - Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength), - "We're either reading or writing, but not both."); - - bool isBlocked = false; - int n = _readLength - _readPos; - // if the read buffer is empty, read into either user's array or our - // buffer, depending on number of bytes user asked for and buffer size. - if (n == 0) - { - if (!CanRead) ThrowHelper.ThrowNotSupportedException_UnreadableStream(); - if (_writePos > 0) FlushWriteBuffer(); - if (!CanSeek || (destination.Length >= _bufferLength)) - { - n = ReadNative(destination); - // Throw away read buffer. - _readPos = 0; - _readLength = 0; - return n; - } - n = ReadNative(GetBuffer()); - if (n == 0) return 0; - isBlocked = n < _bufferLength; - _readPos = 0; - _readLength = n; - } - // Now copy min of count or numBytesAvailable (i.e. near EOF) to array. - if (n > destination.Length) n = destination.Length; - new ReadOnlySpan(GetBuffer(), _readPos, n).CopyTo(destination); - _readPos += n; - - // We may have read less than the number of bytes the user asked - // for, but that is part of the Stream contract. Reading again for - // more data may cause us to block if we're using a device with - // no clear end of file, such as a serial port or pipe. If we - // blocked here & this code was used with redirected pipes for a - // process's standard output, this can lead to deadlocks involving - // two processes. But leave this here for files to avoid what would - // probably be a breaking change. -- - - // If we are reading from a device with no clear EOF like a - // serial port or a pipe, this will cause us to block incorrectly. - if (_fileHandle.CanSeek) - { - // If we hit the end of the buffer and didn't have enough bytes, we must - // read some more from the underlying stream. However, if we got - // fewer bytes from the underlying stream than we asked for (i.e. we're - // probably blocked), don't ask for more bytes. - if (n < destination.Length && !isBlocked) - { - Debug.Assert(_readPos == _readLength, "Read buffer should be empty!"); - int moreBytesRead = ReadNative(destination.Slice(n)); - n += moreBytesRead; - // We've just made our buffer inconsistent with our position - // pointer. We must throw away the read buffer. - _readPos = 0; - _readLength = 0; - } - } - - return n; - } - - [Conditional("DEBUG")] - private void AssertCanRead() - { - Debug.Assert(!_fileHandle.IsClosed, "!_fileHandle.IsClosed"); - Debug.Assert(CanRead, "CanRead"); - } - - /// Reads from the file handle into the buffer, overwriting anything in it. - private int FillReadBufferForReadByte() => - _useAsyncIO ? - ReadNativeAsync(new Memory(_buffer), 0, CancellationToken.None).GetAwaiter().GetResult() : - ReadNative(_buffer); - - private unsafe int ReadNative(Span buffer) - { - Debug.Assert(!_useAsyncIO, $"{nameof(ReadNative)} doesn't work on asynchronous file streams."); - AssertCanRead(); - - // Make sure we are reading from the right spot - VerifyOSHandlePosition(); - - int r = ReadFileNative(_fileHandle, buffer, null, out int errorCode); - - if (r == -1) - { - // For pipes, ERROR_BROKEN_PIPE is the normal end of the pipe. - if (errorCode == Interop.Errors.ERROR_BROKEN_PIPE) - { - r = 0; - } - else - { - if (errorCode == Interop.Errors.ERROR_INVALID_PARAMETER) - ThrowHelper.ThrowArgumentException_HandleNotSync(nameof(_fileHandle)); - - throw Win32Marshal.GetExceptionForWin32Error(errorCode, _fileHandle.Path); - } - } - Debug.Assert(r >= 0, "FileStream's ReadNative is likely broken."); - _filePosition += r; - - return r; - } - - public override long Seek(long offset, SeekOrigin origin) - { - if (origin < SeekOrigin.Begin || origin > SeekOrigin.End) - throw new ArgumentException(SR.Argument_InvalidSeekOrigin, nameof(origin)); - if (_fileHandle.IsClosed) ThrowHelper.ThrowObjectDisposedException_FileClosed(); - if (!CanSeek) ThrowHelper.ThrowNotSupportedException_UnseekableStream(); - - Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength), "We're either reading or writing, but not both."); - - // If we've got bytes in our buffer to write, write them out. - // If we've read in and consumed some bytes, we'll have to adjust - // our seek positions ONLY IF we're seeking relative to the current - // position in the stream. This simulates doing a seek to the new - // position, then a read for the number of bytes we have in our buffer. - if (_writePos > 0) - { - FlushWriteBuffer(); - } - else if (origin == SeekOrigin.Current) - { - // Don't call FlushRead here, which would have caused an infinite - // loop. Simply adjust the seek origin. This isn't necessary - // if we're seeking relative to the beginning or end of the stream. - offset -= (_readLength - _readPos); - } - _readPos = _readLength = 0; - - // Verify that internal position is in sync with the handle - VerifyOSHandlePosition(); - - long oldPos = _filePosition + (_readPos - _readLength); - long pos = SeekCore(_fileHandle, offset, origin); - - // Prevent users from overwriting data in a file that was opened in - // append mode. - if (_appendStart != -1 && pos < _appendStart) - { - SeekCore(_fileHandle, oldPos, SeekOrigin.Begin); - throw new IOException(SR.IO_SeekAppendOverwrite); - } - - // We now must update the read buffer. We can in some cases simply - // update _readPos within the buffer, copy around the buffer so our - // Position property is still correct, and avoid having to do more - // reads from the disk. Otherwise, discard the buffer's contents. - if (_readLength > 0) - { - // We can optimize the following condition: - // oldPos - _readPos <= pos < oldPos + _readLen - _readPos - if (oldPos == pos) - { - if (_readPos > 0) - { - Buffer.BlockCopy(GetBuffer(), _readPos, GetBuffer(), 0, _readLength - _readPos); - _readLength -= _readPos; - _readPos = 0; - } - // If we still have buffered data, we must update the stream's - // position so our Position property is correct. - if (_readLength > 0) - SeekCore(_fileHandle, _readLength, SeekOrigin.Current); - } - else if (oldPos - _readPos < pos && pos < oldPos + _readLength - _readPos) - { - int diff = (int)(pos - oldPos); - Buffer.BlockCopy(GetBuffer(), _readPos + diff, GetBuffer(), 0, _readLength - (_readPos + diff)); - _readLength -= (_readPos + diff); - _readPos = 0; - if (_readLength > 0) - SeekCore(_fileHandle, _readLength, SeekOrigin.Current); - } - else - { - // Lose the read buffer. - _readPos = 0; - _readLength = 0; - } - Debug.Assert(_readLength >= 0 && _readPos <= _readLength, "_readLen should be nonnegative, and _readPos should be less than or equal _readLen"); - Debug.Assert(pos == Position, "Seek optimization: pos != Position! Buffer math was mangled."); - } - return pos; - } - - partial void OnBufferAllocated() - { - Debug.Assert(_buffer != null); - Debug.Assert(_preallocatedOverlapped == null); - - if (_useAsyncIO) - _preallocatedOverlapped = PreAllocatedOverlapped.UnsafeCreate(CompletionSource.s_ioCallback, this, _buffer); - } - - private CompletionSource? CompareExchangeCurrentOverlappedOwner(CompletionSource? newSource, CompletionSource? existingSource) - => Interlocked.CompareExchange(ref _currentOverlappedOwner, newSource, existingSource); - - private void WriteSpan(ReadOnlySpan source) - { - Debug.Assert(!_useAsyncIO, "Must only be used when in synchronous mode"); - - if (_writePos == 0) - { - // Ensure we can write to the stream, and ready buffer for writing. - if (!CanWrite) ThrowHelper.ThrowNotSupportedException_UnwritableStream(); - if (_readPos < _readLength) FlushReadBuffer(); - _readPos = 0; - _readLength = 0; - } - - // If our buffer has data in it, copy data from the user's array into - // the buffer, and if we can fit it all there, return. Otherwise, write - // the buffer to disk and copy any remaining data into our buffer. - // The assumption here is memcpy is cheaper than disk (or net) IO. - // (10 milliseconds to disk vs. ~20-30 microseconds for a 4K memcpy) - // So the extra copying will reduce the total number of writes, in - // non-pathological cases (i.e. write 1 byte, then write for the buffer - // size repeatedly) - if (_writePos > 0) - { - int numBytes = _bufferLength - _writePos; // space left in buffer - if (numBytes > 0) - { - if (numBytes >= source.Length) - { - source.CopyTo(GetBuffer().AsSpan(_writePos)); - _writePos += source.Length; - return; - } - else - { - source.Slice(0, numBytes).CopyTo(GetBuffer().AsSpan(_writePos)); - _writePos += numBytes; - source = source.Slice(numBytes); - } - } - // Reset our buffer. We essentially want to call FlushWrite - // without calling Flush on the underlying Stream. - - WriteCore(new ReadOnlySpan(GetBuffer(), 0, _writePos)); - _writePos = 0; - } - - // If the buffer would slow writes down, avoid buffer completely. - if (source.Length >= _bufferLength) - { - Debug.Assert(_writePos == 0, "FileStream cannot have buffered data to write here! Your stream will be corrupted."); - WriteCore(source); - return; - } - else if (source.Length == 0) - { - return; // Don't allocate a buffer then call memcpy for 0 bytes. - } - - // Copy remaining bytes into buffer, to write at a later date. - source.CopyTo(GetBuffer().AsSpan(_writePos)); - _writePos = source.Length; - return; - } - - private unsafe void WriteCore(ReadOnlySpan source) - { - Debug.Assert(!_useAsyncIO); - Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed"); - Debug.Assert(CanWrite, "_parent.CanWrite"); - Debug.Assert(_readPos == _readLength, "_readPos == _readLen"); - - // Make sure we are writing to the position that we think we are - VerifyOSHandlePosition(); - - int r = WriteFileNative(_fileHandle, source, null, out int errorCode); - - if (r == -1) - { - // For pipes, ERROR_NO_DATA is not an error, but the pipe is closing. - if (errorCode == Interop.Errors.ERROR_NO_DATA) - { - r = 0; - } - else - { - // ERROR_INVALID_PARAMETER may be returned for writes - // where the position is too large or for synchronous writes - // to a handle opened asynchronously. - if (errorCode == Interop.Errors.ERROR_INVALID_PARAMETER) - throw new IOException(SR.IO_FileTooLongOrHandleNotSync); - throw Win32Marshal.GetExceptionForWin32Error(errorCode, _fileHandle.Path); - } - } - Debug.Assert(r >= 0, "FileStream's WriteCore is likely broken."); - _filePosition += r; - return; - } - - private Task? ReadAsyncInternal(Memory destination, CancellationToken cancellationToken, out int synchronousResult) - { - Debug.Assert(_useAsyncIO); - if (!CanRead) ThrowHelper.ThrowNotSupportedException_UnreadableStream(); - - Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength), "We're either reading or writing, but not both."); - - if (!_fileHandle.CanSeek) - { - // Pipes are tricky, at least when you have 2 different pipes - // that you want to use simultaneously. When redirecting stdout - // & stderr with the Process class, it's easy to deadlock your - // parent & child processes when doing writes 4K at a time. The - // OS appears to use a 4K buffer internally. If you write to a - // pipe that is full, you will block until someone read from - // that pipe. If you try reading from an empty pipe and - // Win32FileStream's ReadAsync blocks waiting for data to fill it's - // internal buffer, you will be blocked. In a case where a child - // process writes to stdout & stderr while a parent process tries - // reading from both, you can easily get into a deadlock here. - // To avoid this deadlock, don't buffer when doing async IO on - // pipes. But don't completely ignore buffered data either. - if (_readPos < _readLength) - { - int n = Math.Min(_readLength - _readPos, destination.Length); - new Span(GetBuffer(), _readPos, n).CopyTo(destination.Span); - _readPos += n; - synchronousResult = n; - return null; - } - else - { - Debug.Assert(_writePos == 0, "Win32FileStream must not have buffered write data here! Pipes should be unidirectional."); - synchronousResult = 0; - return ReadNativeAsync(destination, 0, cancellationToken); - } - } - - Debug.Assert(_fileHandle.CanSeek, "Should be seekable"); - - // Handle buffering. - if (_writePos > 0) FlushWriteBuffer(); - if (_readPos == _readLength) - { - // I can't see how to handle buffering of async requests when - // filling the buffer asynchronously, without a lot of complexity. - // The problems I see are issuing an async read, we do an async - // read to fill the buffer, then someone issues another read - // (either synchronously or asynchronously) before the first one - // returns. This would involve some sort of complex buffer locking - // that we probably don't want to get into, at least not in V1. - // If we did a sync read to fill the buffer, we could avoid the - // problem, and any async read less than 64K gets turned into a - // synchronous read by NT anyways... -- - - if (destination.Length < _bufferLength) - { - Task readTask = ReadNativeAsync(new Memory(GetBuffer()), 0, cancellationToken); - _readLength = readTask.GetAwaiter().GetResult(); - int n = Math.Min(_readLength, destination.Length); - new Span(GetBuffer(), 0, n).CopyTo(destination.Span); - _readPos = n; - - synchronousResult = n; - return null; - } - else - { - // Here we're making our position pointer inconsistent - // with our read buffer. Throw away the read buffer's contents. - _readPos = 0; - _readLength = 0; - synchronousResult = 0; - return ReadNativeAsync(destination, 0, cancellationToken); - } - } - else - { - int n = Math.Min(_readLength - _readPos, destination.Length); - new Span(GetBuffer(), _readPos, n).CopyTo(destination.Span); - _readPos += n; - - if (n == destination.Length) - { - // Return a completed task - synchronousResult = n; - return null; - } - else - { - // For streams with no clear EOF like serial ports or pipes - // we cannot read more data without causing an app to block - // incorrectly. Pipes don't go down this path - // though. This code needs to be fixed. - // Throw away read buffer. - _readPos = 0; - _readLength = 0; - synchronousResult = 0; - return ReadNativeAsync(destination.Slice(n), n, cancellationToken); - } - } - } - - private unsafe Task ReadNativeAsync(Memory destination, int numBufferedBytesRead, CancellationToken cancellationToken) - { - AssertCanRead(); - Debug.Assert(_useAsyncIO, "ReadNativeAsync doesn't work on synchronous file streams!"); - - // Create and store async stream class library specific data in the async result - CompletionSource completionSource = CompletionSource.Create(this, _preallocatedOverlapped, numBufferedBytesRead, destination); - NativeOverlapped* intOverlapped = completionSource.Overlapped; - - // Calculate position in the file we should be at after the read is done - if (CanSeek) - { - long len = Length; - - // Make sure we are reading from the position that we think we are - VerifyOSHandlePosition(); - - if (_filePosition + destination.Length > len) - { - if (_filePosition <= len) - { - destination = destination.Slice(0, (int)(len - _filePosition)); - } - else - { - destination = default; - } - } - - // Now set the position to read from in the NativeOverlapped struct - // For pipes, we should leave the offset fields set to 0. - intOverlapped->OffsetLow = unchecked((int)_filePosition); - intOverlapped->OffsetHigh = (int)(_filePosition >> 32); - - // When using overlapped IO, the OS is not supposed to - // touch the file pointer location at all. We will adjust it - // ourselves. This isn't threadsafe. - - // WriteFile should not update the file pointer when writing - // in overlapped mode, according to MSDN. But it does update - // the file pointer when writing to a UNC path! - // So changed the code below to seek to an absolute - // location, not a relative one. ReadFile seems consistent though. - SeekCore(_fileHandle, destination.Length, SeekOrigin.Current); - } - - // queue an async ReadFile operation and pass in a packed overlapped - int r = ReadFileNative(_fileHandle, destination.Span, intOverlapped, out int errorCode); - - // ReadFile, the OS version, will return 0 on failure. But - // my ReadFileNative wrapper returns -1. My wrapper will return - // the following: - // On error, r==-1. - // On async requests that are still pending, r==-1 w/ errorCode==ERROR_IO_PENDING - // on async requests that completed sequentially, r==0 - // You will NEVER RELIABLY be able to get the number of bytes - // read back from this call when using overlapped structures! You must - // not pass in a non-null lpNumBytesRead to ReadFile when using - // overlapped structures! This is by design NT behavior. - if (r == -1) - { - // For pipes, when they hit EOF, they will come here. - if (errorCode == Interop.Errors.ERROR_BROKEN_PIPE) - { - // Not an error, but EOF. AsyncFSCallback will NOT be - // called. Call the user callback here. - - // We clear the overlapped status bit for this special case. - // Failure to do so looks like we are freeing a pending overlapped later. - intOverlapped->InternalLow = IntPtr.Zero; - completionSource.SetCompletedSynchronously(0); - } - else if (errorCode != Interop.Errors.ERROR_IO_PENDING) - { - if (!_fileHandle.IsClosed && CanSeek) // Update Position - It could be anywhere. - { - SeekCore(_fileHandle, 0, SeekOrigin.Current); - } - - completionSource.ReleaseNativeResource(); - - if (errorCode == Interop.Errors.ERROR_HANDLE_EOF) - { - ThrowHelper.ThrowEndOfFileException(); - } - else - { - throw Win32Marshal.GetExceptionForWin32Error(errorCode, _fileHandle.Path); - } - } - else if (cancellationToken.CanBeCanceled) // ERROR_IO_PENDING - { - // Only once the IO is pending do we register for cancellation - completionSource.RegisterForCancellation(cancellationToken); - } - } - else - { - // Due to a workaround for a race condition in NT's ReadFile & - // WriteFile routines, we will always be returning 0 from ReadFileNative - // when we do async IO instead of the number of bytes read, - // irregardless of whether the operation completed - // synchronously or asynchronously. We absolutely must not - // set asyncResult._numBytes here, since will never have correct - // results. - } - - return completionSource.Task; - } - - private ValueTask WriteAsyncInternal(ReadOnlyMemory source, CancellationToken cancellationToken) - { - Debug.Assert(_useAsyncIO); - Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength), "We're either reading or writing, but not both."); - Debug.Assert(_fileHandle.CanSeek || (_readPos == 0 && _readLength == 0), "Win32FileStream must not have buffered data here! Pipes should be unidirectional."); - - if (!CanWrite) ThrowHelper.ThrowNotSupportedException_UnwritableStream(); - - bool writeDataStoredInBuffer = false; - if (_fileHandle.CanSeek) // avoid async buffering with non-seekable files (e.g. pipes), as doing so can lead to deadlocks (see comments in ReadInternalAsyncCore) - { - // Ensure the buffer is clear for writing - if (_writePos == 0) - { - if (_readPos < _readLength) - { - FlushReadBuffer(); - } - _readPos = 0; - _readLength = 0; - } - - // Determine how much space remains in the buffer - int remainingBuffer = _bufferLength - _writePos; - Debug.Assert(remainingBuffer >= 0); - - // Simple/common case: - // - The write is smaller than our buffer, such that it's worth considering buffering it. - // - There's no active flush operation, such that we don't have to worry about the existing buffer being in use. - // - And the data we're trying to write fits in the buffer, meaning it wasn't already filled by previous writes. - // In that case, just store it in the buffer. - if (source.Length < _bufferLength && !HasActiveBufferOperation && source.Length <= remainingBuffer) - { - source.Span.CopyTo(new Span(GetBuffer(), _writePos, source.Length)); - _writePos += source.Length; - writeDataStoredInBuffer = true; - - // There is one special-but-common case, common because devs often use - // byte[] sizes that are powers of 2 and thus fit nicely into our buffer, which is - // also a power of 2. If after our write the buffer still has remaining space, - // then we're done and can return a completed task now. But if we filled the buffer - // completely, we want to do the asynchronous flush/write as part of this operation - // rather than waiting until the next write that fills the buffer. - if (source.Length != remainingBuffer) - return default; - - Debug.Assert(_writePos == _bufferLength); - } - } - - // At this point, at least one of the following is true: - // 1. There was an active flush operation (it could have completed by now, though). - // 2. The data doesn't fit in the remaining buffer (or it's a pipe and we chose not to try). - // 3. We wrote all of the data to the buffer, filling it. - // - // If there's an active operation, we can't touch the current buffer because it's in use. - // That gives us a choice: we can either allocate a new buffer, or we can skip the buffer - // entirely (even if the data would otherwise fit in it). For now, for simplicity, we do - // the latter; it could also have performance wins due to OS-level optimizations, and we could - // potentially add support for PreAllocatedOverlapped due to having a single buffer. (We can - // switch to allocating a new buffer, potentially experimenting with buffer pooling, should - // performance data suggest it's appropriate.) - // - // If the data doesn't fit in the remaining buffer, it could be because it's so large - // it's greater than the entire buffer size, in which case we'd always skip the buffer, - // or it could be because there's more data than just the space remaining. For the latter - // case, we need to issue an asynchronous write to flush that data, which then turns this into - // the first case above with an active operation. - // - // If we already stored the data, then we have nothing additional to write beyond what - // we need to flush. - // - // In any of these cases, we have the same outcome: - // - If there's data in the buffer, flush it by writing it out asynchronously. - // - Then, if there's any data to be written, issue a write for it concurrently. - // We return a Task that represents one or both. - - // Flush the buffer asynchronously if there's anything to flush - Task? flushTask = null; - if (_writePos > 0) - { - flushTask = FlushWriteAsync(cancellationToken); - - // If we already copied all of the data into the buffer, - // simply return the flush task here. Same goes for if the task has - // already completed and was unsuccessful. - if (writeDataStoredInBuffer || - flushTask.IsFaulted || - flushTask.IsCanceled) - { - return new ValueTask(flushTask); - } - } - - Debug.Assert(!writeDataStoredInBuffer); - Debug.Assert(_writePos == 0); - - // Finally, issue the write asynchronously, and return a Task that logically - // represents the write operation, including any flushing done. - Task writeTask = WriteAsyncInternalCore(source, cancellationToken); - return new ValueTask( - (flushTask == null || flushTask.Status == TaskStatus.RanToCompletion) ? writeTask : - (writeTask.Status == TaskStatus.RanToCompletion) ? flushTask : - Task.WhenAll(flushTask, writeTask)); - } - - private unsafe Task WriteAsyncInternalCore(ReadOnlyMemory source, CancellationToken cancellationToken) - { - Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed"); - Debug.Assert(CanWrite, "_parent.CanWrite"); - Debug.Assert(_readPos == _readLength, "_readPos == _readLen"); - Debug.Assert(_useAsyncIO, "WriteInternalCoreAsync doesn't work on synchronous file streams!"); - - // Create and store async stream class library specific data in the async result - CompletionSource completionSource = CompletionSource.Create(this, _preallocatedOverlapped, 0, source); - NativeOverlapped* intOverlapped = completionSource.Overlapped; - - if (CanSeek) - { - // Make sure we set the length of the file appropriately. - long len = Length; - - // Make sure we are writing to the position that we think we are - VerifyOSHandlePosition(); - - if (_filePosition + source.Length > len) - { - SetLengthCore(_filePosition + source.Length); - } - - // Now set the position to read from in the NativeOverlapped struct - // For pipes, we should leave the offset fields set to 0. - intOverlapped->OffsetLow = (int)_filePosition; - intOverlapped->OffsetHigh = (int)(_filePosition >> 32); - - // When using overlapped IO, the OS is not supposed to - // touch the file pointer location at all. We will adjust it - // ourselves. This isn't threadsafe. - SeekCore(_fileHandle, source.Length, SeekOrigin.Current); - } - - // queue an async WriteFile operation and pass in a packed overlapped - int r = WriteFileNative(_fileHandle, source.Span, intOverlapped, out int errorCode); - - // WriteFile, the OS version, will return 0 on failure. But - // my WriteFileNative wrapper returns -1. My wrapper will return - // the following: - // On error, r==-1. - // On async requests that are still pending, r==-1 w/ errorCode==ERROR_IO_PENDING - // On async requests that completed sequentially, r==0 - // You will NEVER RELIABLY be able to get the number of bytes - // written back from this call when using overlapped IO! You must - // not pass in a non-null lpNumBytesWritten to WriteFile when using - // overlapped structures! This is ByDesign NT behavior. - if (r == -1) - { - // For pipes, when they are closed on the other side, they will come here. - if (errorCode == Interop.Errors.ERROR_NO_DATA) - { - // Not an error, but EOF. AsyncFSCallback will NOT be called. - // Completing TCS and return cached task allowing the GC to collect TCS. - completionSource.SetCompletedSynchronously(0); - return Task.CompletedTask; - } - else if (errorCode != Interop.Errors.ERROR_IO_PENDING) - { - if (!_fileHandle.IsClosed && CanSeek) // Update Position - It could be anywhere. - { - SeekCore(_fileHandle, 0, SeekOrigin.Current); - } - - completionSource.ReleaseNativeResource(); - - if (errorCode == Interop.Errors.ERROR_HANDLE_EOF) - { - ThrowHelper.ThrowEndOfFileException(); - } - else - { - throw Win32Marshal.GetExceptionForWin32Error(errorCode, _fileHandle.Path); - } - } - else if (cancellationToken.CanBeCanceled) // ERROR_IO_PENDING - { - // Only once the IO is pending do we register for cancellation - completionSource.RegisterForCancellation(cancellationToken); - } - } - else - { - // Due to a workaround for a race condition in NT's ReadFile & - // WriteFile routines, we will always be returning 0 from WriteFileNative - // when we do async IO instead of the number of bytes written, - // irregardless of whether the operation completed - // synchronously or asynchronously. We absolutely must not - // set asyncResult._numBytes here, since will never have correct - // results. - } - - return completionSource.Task; - } - - // __ConsoleStream also uses this code. - private unsafe int ReadFileNative(SafeFileHandle handle, Span bytes, NativeOverlapped* overlapped, out int errorCode) - { - Debug.Assert((_useAsyncIO && overlapped != null) || (!_useAsyncIO && overlapped == null), "Async IO and overlapped parameters inconsistent in call to ReadFileNative."); - - return FileStreamHelpers.ReadFileNative(handle, bytes, overlapped, out errorCode); - } - - private unsafe int WriteFileNative(SafeFileHandle handle, ReadOnlySpan buffer, NativeOverlapped* overlapped, out int errorCode) - { - Debug.Assert((_useAsyncIO && overlapped != null) || (!_useAsyncIO && overlapped == null), "Async IO and overlapped parameters inconsistent in call to WriteFileNative."); - - int numBytesWritten = 0; - int r; - - fixed (byte* p = &MemoryMarshal.GetReference(buffer)) - { - r = overlapped == null - ? Interop.Kernel32.WriteFile(handle, p, buffer.Length, out numBytesWritten, overlapped) - : Interop.Kernel32.WriteFile(handle, p, buffer.Length, IntPtr.Zero, overlapped); - } - - if (r == 0) - { - errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(handle); - return -1; - } - else - { - errorCode = 0; - return numBytesWritten; - } - } - - public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) - { - // If we're in sync mode, just use the shared CopyToAsync implementation that does - // typical read/write looping. - if (!_useAsyncIO) - { - return base.CopyToAsync(destination, bufferSize, cancellationToken); - } - - // Fail if the file was closed - if (_fileHandle.IsClosed) - { - ThrowHelper.ThrowObjectDisposedException_FileClosed(); - } - if (!CanRead) - { - ThrowHelper.ThrowNotSupportedException_UnreadableStream(); - } - - // Bail early for cancellation if cancellation has been requested - if (cancellationToken.IsCancellationRequested) - { - return Task.FromCanceled(cancellationToken); - } - - // Do the async copy, with differing implementations based on whether the FileStream was opened as async or sync - Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength), "We're either reading or writing, but not both."); - return AsyncModeCopyToAsync(destination, bufferSize, cancellationToken); - } - - private async Task AsyncModeCopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) - { - Debug.Assert(_useAsyncIO, "This implementation is for async mode only"); - Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed"); - Debug.Assert(CanRead, "_parent.CanRead"); - - // Make sure any pending writes have been flushed before we do a read. - if (_writePos > 0) - { - await FlushWriteAsync(cancellationToken).ConfigureAwait(false); - } - - // Typically CopyToAsync would be invoked as the only "read" on the stream, but it's possible some reading is - // done and then the CopyToAsync is issued. For that case, see if we have any data available in the buffer. - if (GetBuffer() != null) - { - int bufferedBytes = _readLength - _readPos; - if (bufferedBytes > 0) - { - await destination.WriteAsync(new ReadOnlyMemory(GetBuffer(), _readPos, bufferedBytes), cancellationToken).ConfigureAwait(false); - _readPos = _readLength = 0; - } - } - - bool canSeek = CanSeek; - if (canSeek) - { - VerifyOSHandlePosition(); - } - - try - { - await FileStreamHelpers - .AsyncModeCopyToAsync(_fileHandle, canSeek, _filePosition, destination, bufferSize, cancellationToken) - .ConfigureAwait(false); - } - finally - { - // Make sure the stream's current position reflects where we ended up - if (!_fileHandle.IsClosed && CanSeek) - { - SeekCore(_fileHandle, 0, SeekOrigin.End); - } - } - } - - internal override void Lock(long position, long length) => FileStreamHelpers.Lock(_fileHandle, CanWrite, position, length); - - internal override void Unlock(long position, long length) => FileStreamHelpers.Unlock(_fileHandle, position, length); - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.cs deleted file mode 100644 index 797f6f5e9c0ce..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.cs +++ /dev/null @@ -1,536 +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.Diagnostics; -using System.Runtime.InteropServices; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Win32.SafeHandles; - -namespace System.IO.Strategies -{ - // This type is partial so we can avoid code duplication between Windows and Unix Net5Compat implementations - internal sealed partial class Net5CompatFileStreamStrategy : FileStreamStrategy - { - private byte[]? _buffer; - private readonly int _bufferLength; - private readonly SafeFileHandle _fileHandle; // only ever null if ctor throws - - /// Whether the file is opened for reading, writing, or both. - private readonly FileAccess _access; - - /// The next available byte to be read from the _buffer. - private int _readPos; - - /// The number of valid bytes in _buffer. - private int _readLength; - - /// The next location in which a write should occur to the buffer. - private int _writePos; - - /// - /// Whether asynchronous read/write/flush operations should be performed using async I/O. - /// On Windows FileOptions.Asynchronous controls how the file handle is configured, - /// and then as a result how operations are issued against that file handle. On Unix, - /// there isn't any distinction around how file descriptors are created for async vs - /// sync, but we still differentiate how the operations are issued in order to provide - /// similar behavioral semantics and performance characteristics as on Windows. On - /// Windows, if non-async, async read/write requests just delegate to the base stream, - /// and no attempt is made to synchronize between sync and async operations on the stream; - /// if async, then async read/write requests are implemented specially, and sync read/write - /// requests are coordinated with async ones by implementing the sync ones over the async - /// ones. On Unix, we do something similar. If non-async, async read/write requests just - /// delegate to the base stream, and no attempt is made to synchronize. If async, we use - /// a semaphore to coordinate both sync and async operations. - /// - private readonly bool _useAsyncIO; - - /// cached task for read ops that complete synchronously - private Task? _lastSynchronouslyCompletedTask; - - /// - /// Currently cached position in the stream. This should always mirror the underlying file's actual position, - /// and should only ever be out of sync if another stream with access to this same file manipulates it, at which - /// point we attempt to error out. - /// - private long _filePosition; - - /// Whether the file stream's handle has been exposed. - private bool _exposedHandle; - - internal Net5CompatFileStreamStrategy(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) - { - _exposedHandle = true; - _bufferLength = bufferSize; - - InitFromHandle(handle, access, isAsync); - - // Note: It would be cleaner to set the following fields in ValidateHandle, - // but we can't as they're readonly. - _access = access; - _useAsyncIO = isAsync; - - // As the handle was passed in, we must set the handle field at the very end to - // avoid the finalizer closing the handle when we throw errors. - _fileHandle = handle; - } - - internal Net5CompatFileStreamStrategy(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options, long preallocationSize) - { - string fullPath = Path.GetFullPath(path); - - _access = access; - _bufferLength = bufferSize; - - if ((options & FileOptions.Asynchronous) != 0) - _useAsyncIO = true; - - _fileHandle = SafeFileHandle.Open(fullPath, mode, access, share, options, preallocationSize); - - try - { - Init(mode, path, options); - } - catch - { - // If anything goes wrong while setting up the stream, make sure we deterministically dispose - // of the opened handle. - _fileHandle.Dispose(); - _fileHandle = null!; - throw; - } - } - - ~Net5CompatFileStreamStrategy() => Dispose(false); // mandatory to Flush the write buffer - - internal override void DisposeInternal(bool disposing) => Dispose(disposing); - - public override Task FlushAsync(CancellationToken cancellationToken) - { - // TODO: https://github.com/dotnet/runtime/issues/27643 (stop doing this synchronous work!!). - // The always synchronous data transfer between the OS and the internal buffer is intentional - // because this is needed to allow concurrent async IO requests. Concurrent data transfer - // between the OS and the internal buffer will result in race conditions. Since FlushWrite and - // FlushRead modify internal state of the stream and transfer data between the OS and the - // internal buffer, they cannot be truly async. We will, however, flush the OS file buffers - // asynchronously because it doesn't modify any internal state of the stream and is potentially - // a long running process. - try - { - FlushInternalBuffer(); - } - catch (Exception e) - { - return Task.FromException(e); - } - - return Task.CompletedTask; - } - - public override int Read(byte[] buffer, int offset, int count) - { - return _useAsyncIO ? - ReadAsyncTask(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult() : - ReadSpan(new Span(buffer, offset, count)); - } - - public override int Read(Span buffer) - { - if (!_useAsyncIO) - { - if (_fileHandle.IsClosed) - { - ThrowHelper.ThrowObjectDisposedException_FileClosed(); - } - - return ReadSpan(buffer); - } - - // If the stream is in async mode, we can't call the synchronous ReadSpan, so we similarly call the base Read, - // which will turn delegate to Read(byte[],int,int), which will do the right thing if we're in async mode. - return base.Read(buffer); - } - - public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - if (!_useAsyncIO) - { - // If we weren't opened for asynchronous I/O, we still call to the base implementation so that - // Read is invoked asynchronously. But we can do so using the base Stream's internal helper - // that bypasses delegating to BeginRead, since we already know this is FileStream rather - // than something derived from it and what our BeginRead implementation is going to do. - return BeginReadInternal(buffer, offset, count, null, null, serializeAsynchronously: true, apm: false); - } - - return ReadAsyncTask(buffer, offset, count, cancellationToken); - } - - public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) - { - if (!_useAsyncIO) - { - // If we weren't opened for asynchronous I/O, we still call to the base implementation so that - // Read is invoked asynchronously. But if we have a byte[], we can do so using the base Stream's - // internal helper that bypasses delegating to BeginRead, since we already know this is FileStream - // rather than something derived from it and what our BeginRead implementation is going to do. - return MemoryMarshal.TryGetArray(buffer, out ArraySegment segment) ? - new ValueTask(BeginReadInternal(segment.Array!, segment.Offset, segment.Count, null, null, serializeAsynchronously: true, apm: false)) : - base.ReadAsync(buffer, cancellationToken); - } - - Task? t = ReadAsyncInternal(buffer, cancellationToken, out int synchronousResult); - return t != null ? - new ValueTask(t) : - new ValueTask(synchronousResult); - } - - private Task ReadAsyncTask(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - Task? t = ReadAsyncInternal(new Memory(buffer, offset, count), cancellationToken, out int synchronousResult); - - if (t == null) - { - t = _lastSynchronouslyCompletedTask; - Debug.Assert(t == null || t.IsCompletedSuccessfully, "Cached task should have completed successfully"); - - if (t == null || t.Result != synchronousResult) - { - _lastSynchronouslyCompletedTask = t = Task.FromResult(synchronousResult); - } - } - - return t; - } - - public override void Write(byte[] buffer, int offset, int count) - { - if (_useAsyncIO) - { - WriteAsyncInternal(new ReadOnlyMemory(buffer, offset, count), CancellationToken.None).AsTask().GetAwaiter().GetResult(); - } - else - { - WriteSpan(new ReadOnlySpan(buffer, offset, count)); - } - } - - public override void Write(ReadOnlySpan buffer) - { - if (!_useAsyncIO) - { - if (_fileHandle.IsClosed) - { - ThrowHelper.ThrowObjectDisposedException_FileClosed(); - } - - WriteSpan(buffer); - } - else - { - // If the stream is in async mode, we can't call the synchronous WriteSpan, so we similarly call the base Write, - // which will turn delegate to Write(byte[],int,int), which will do the right thing if we're in async mode. - base.Write(buffer); - } - } - - public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - if (!_useAsyncIO) - { - // If we weren't opened for asynchronous I/O, we still call to the base implementation so that - // Write is invoked asynchronously. But we can do so using the base Stream's internal helper - // that bypasses delegating to BeginWrite, since we already know this is FileStream rather - // than something derived from it and what our BeginWrite implementation is going to do. - return BeginWriteInternal(buffer, offset, count, null, null, serializeAsynchronously: true, apm: false); - } - - return WriteAsyncInternal(new ReadOnlyMemory(buffer, offset, count), cancellationToken).AsTask(); - } - - public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) - { - if (!_useAsyncIO) - { - // If we weren't opened for asynchronous I/O, we still call to the base implementation so that - // Write is invoked asynchronously. But if we have a byte[], we can do so using the base Stream's - // internal helper that bypasses delegating to BeginWrite, since we already know this is FileStream - // rather than something derived from it and what our BeginWrite implementation is going to do. - return MemoryMarshal.TryGetArray(buffer, out ArraySegment segment) ? - new ValueTask(BeginWriteInternal(segment.Array!, segment.Offset, segment.Count, null, null, serializeAsynchronously: true, apm: false)) : - base.WriteAsync(buffer, cancellationToken); - } - - return WriteAsyncInternal(buffer, cancellationToken); - } - - public override void Flush() => Flush(flushToDisk: false); - - internal override void Flush(bool flushToDisk) - { - FlushInternalBuffer(); - - if (flushToDisk && CanWrite) - { - FileStreamHelpers.FlushToDisk(_fileHandle); - } - } - - public override bool CanRead => !_fileHandle.IsClosed && (_access & FileAccess.Read) != 0; - - public override bool CanWrite => !_fileHandle.IsClosed && (_access & FileAccess.Write) != 0; - - internal override SafeFileHandle SafeFileHandle - { - get - { - Flush(); - _exposedHandle = true; - return _fileHandle; - } - } - - internal override string Name => _fileHandle.Path ?? SR.IO_UnknownFileName; - - internal override bool IsAsync => _useAsyncIO; - - /// - /// Verify that the actual position of the OS's handle equals what we expect it to. - /// This will fail if someone else moved the UnixFileStream's handle or if - /// our position updating code is incorrect. - /// - private void VerifyOSHandlePosition() - { - bool verifyPosition = _exposedHandle; // in release, only verify if we've given out the handle such that someone else could be manipulating it -#if DEBUG - verifyPosition = true; // in debug, always make sure our position matches what the OS says it should be -#endif - if (verifyPosition && CanSeek) - { - long oldPos = _filePosition; // SeekCore will override the current _position, so save it now - long curPos = SeekCore(_fileHandle, 0, SeekOrigin.Current); - if (oldPos != curPos) - { - // For reads, this is non-fatal but we still could have returned corrupted - // data in some cases, so discard the internal buffer. For writes, - // this is a problem; discard the buffer and error out. - _readPos = _readLength = 0; - if (_writePos > 0) - { - _writePos = 0; - throw new IOException(SR.IO_FileStreamHandlePosition); - } - } - } - } - - /// Verifies that state relating to the read/write buffer is consistent. - [Conditional("DEBUG")] - private void AssertBufferInvariants() - { - // Read buffer values must be in range: 0 <= _bufferReadPos <= _bufferReadLength <= _bufferLength - Debug.Assert(0 <= _readPos && _readPos <= _readLength && _readLength <= _bufferLength); - - // Write buffer values must be in range: 0 <= _bufferWritePos <= _bufferLength - Debug.Assert(0 <= _writePos && _writePos <= _bufferLength); - - // Read buffering and write buffering can't both be active - Debug.Assert((_readPos == 0 && _readLength == 0) || _writePos == 0); - } - - /// Validates that we're ready to read from the stream. - private void PrepareForReading() - { - if (_fileHandle.IsClosed) - ThrowHelper.ThrowObjectDisposedException_FileClosed(); - if (_readLength == 0 && !CanRead) - ThrowHelper.ThrowNotSupportedException_UnreadableStream(); - - AssertBufferInvariants(); - } - - /// Gets or sets the position within the current stream - public override long Position - { - get - { - AssertBufferInvariants(); - VerifyOSHandlePosition(); - - // We may have read data into our buffer from the handle, such that the handle position - // is artificially further along than the consumer's view of the stream's position. - // Thus, when reading, our position is really starting from the handle position negatively - // offset by the number of bytes in the buffer and positively offset by the number of - // bytes into that buffer we've read. When writing, both the read length and position - // must be zero, and our position is just the handle position offset positive by how many - // bytes we've written into the buffer. - return (_filePosition - _readLength) + _readPos + _writePos; - } - set - { - Seek(value, SeekOrigin.Begin); - } - } - - // This doesn't do argument checking. Necessary for SetLength, which must - // set the file pointer beyond the end of the file. This will update the - // internal position - private long SeekCore(SafeFileHandle fileHandle, long offset, SeekOrigin origin, bool closeInvalidHandle = false) - { - Debug.Assert(fileHandle.CanSeek, "fileHandle.CanSeek"); - - return _filePosition = FileStreamHelpers.Seek(fileHandle, offset, origin, closeInvalidHandle); - } - - internal override bool IsClosed => _fileHandle.IsClosed; - - /// - /// Gets the array used for buffering reading and writing. - /// If the array hasn't been allocated, this will lazily allocate it. - /// - /// The buffer. - private byte[] GetBuffer() - { - Debug.Assert(_buffer == null || _buffer.Length == _bufferLength); - if (_buffer == null) - { - _buffer = new byte[_bufferLength]; - OnBufferAllocated(); - } - - return _buffer; - } - - /// - /// Flushes the internal read/write buffer for this stream. If write data has been buffered, - /// that data is written out to the underlying file. Or if data has been buffered for - /// reading from the stream, the data is dumped and our position in the underlying file - /// is rewound as necessary. This does not flush the OS buffer. - /// - private void FlushInternalBuffer() - { - AssertBufferInvariants(); - if (_writePos > 0) - { - FlushWriteBuffer(); - } - else if (_readPos < _readLength && CanSeek) - { - FlushReadBuffer(); - } - } - - /// Dumps any read data in the buffer and rewinds our position in the stream, accordingly, as necessary. - private void FlushReadBuffer() - { - // Reading is done by blocks from the file, but someone could read - // 1 byte from the buffer then write. At that point, the OS's file - // pointer is out of sync with the stream's position. All write - // functions should call this function to preserve the position in the file. - - AssertBufferInvariants(); - Debug.Assert(_writePos == 0, "FileStream: Write buffer must be empty in FlushReadBuffer!"); - - int rewind = _readPos - _readLength; - if (rewind != 0) - { - Debug.Assert(CanSeek, "FileStream will lose buffered read data now."); - SeekCore(_fileHandle, rewind, SeekOrigin.Current); - } - _readPos = _readLength = 0; - } - - /// - /// Reads a byte from the file stream. Returns the byte cast to an int - /// or -1 if reading from the end of the stream. - /// - public override int ReadByte() - { - PrepareForReading(); - - byte[] buffer = GetBuffer(); - if (_readPos == _readLength) - { - FlushWriteBuffer(); - _readLength = FillReadBufferForReadByte(); - _readPos = 0; - if (_readLength == 0) - { - return -1; - } - } - - return buffer[_readPos++]; - } - - /// - /// Writes a byte to the current position in the stream and advances the position - /// within the stream by one byte. - /// - /// The byte to write to the stream. - public override void WriteByte(byte value) - { - PrepareForWriting(); - - // Flush the write buffer if it's full - if (_writePos == _bufferLength) - FlushWriteBufferForWriteByte(); - - // We now have space in the buffer. Store the byte. - GetBuffer()[_writePos++] = value; - } - - /// - /// Validates that we're ready to write to the stream, - /// including flushing a read buffer if necessary. - /// - private void PrepareForWriting() - { - if (_fileHandle.IsClosed) - ThrowHelper.ThrowObjectDisposedException_FileClosed(); - - // Make sure we're good to write. We only need to do this if there's nothing already - // in our write buffer, since if there is something in the buffer, we've already done - // this checking and flushing. - if (_writePos == 0) - { - if (!CanWrite) ThrowHelper.ThrowNotSupportedException_UnwritableStream(); - FlushReadBuffer(); - Debug.Assert(_bufferLength > 0, "_bufferSize > 0"); - } - } - - partial void OnBufferAllocated(); - - public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) - { - if (!_useAsyncIO) - return base.BeginRead(buffer, offset, count, callback, state); - else - return TaskToApm.Begin(ReadAsyncTask(buffer, offset, count, CancellationToken.None), callback, state); - } - - public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) - { - if (!_useAsyncIO) - return base.BeginWrite(buffer, offset, count, callback, state); - else - return TaskToApm.Begin(WriteAsyncInternal(new ReadOnlyMemory(buffer, offset, count), CancellationToken.None).AsTask(), callback, state); - } - - public override int EndRead(IAsyncResult asyncResult) - { - if (!_useAsyncIO) - return base.EndRead(asyncResult); - else - return TaskToApm.End(asyncResult); - } - - public override void EndWrite(IAsyncResult asyncResult) - { - if (!_useAsyncIO) - base.EndWrite(asyncResult); - else - TaskToApm.End(asyncResult); - } - } -} From 9fbc728a936137b6ca4b4e384984a4e19e13bc9b Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Sat, 9 Oct 2021 19:03:40 +0300 Subject: [PATCH 024/106] [mono][interp] Fix unboxing during CALLI (#59747) * [tests] Re-enable test * [interp] Fix unboxing during CALLI On interp we were storing function pointers as a simple InterpMethod. The problem is that, when loading an InterpMethod with a ldvirtftn opcode, the resulting method might need to have its this pointer unboxed, so this information needs to be embedded into the function pointer. We achieve this by tagging the InterpMethod whether we need to perform unboxing as part of calling it. --- src/mono/mono/mini/interp/interp-internals.h | 5 ++ src/mono/mono/mini/interp/interp.c | 68 +++++++++++--------- src/mono/mono/mini/interp/mintops.def | 2 +- src/mono/mono/mini/interp/transform.c | 1 - src/mono/mono/mini/mini.h | 2 +- src/tests/issues.targets | 3 - 6 files changed, 46 insertions(+), 35 deletions(-) diff --git a/src/mono/mono/mini/interp/interp-internals.h b/src/mono/mono/mini/interp/interp-internals.h index 465fd20240fa4..8db776412fef2 100644 --- a/src/mono/mono/mini/interp/interp-internals.h +++ b/src/mono/mono/mini/interp/interp-internals.h @@ -97,6 +97,10 @@ typedef enum { #define PROFILE_INTERP 0 +#define INTERP_IMETHOD_TAG_UNBOX(im) ((gpointer)((mono_u)(im) | 1)) +#define INTERP_IMETHOD_IS_TAGGED_UNBOX(im) ((mono_u)(im) & 1) +#define INTERP_IMETHOD_UNTAG_UNBOX(im) ((InterpMethod*)((mono_u)(im) & ~1)) + /* * Structure representing a method transformed for the interpreter */ @@ -125,6 +129,7 @@ struct InterpMethod { MonoType **param_types; MonoJitInfo *jinfo; MonoFtnDesc *ftndesc; + MonoFtnDesc *ftndesc_unbox; guint32 locals_size; guint32 alloca_size; diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index 61b685cb307c8..7ad368990b162 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -1679,7 +1679,7 @@ interp_init_delegate (MonoDelegate *del, MonoError *error) /* Convert a function pointer for a managed method to an InterpMethod* */ static InterpMethod* -ftnptr_to_imethod (gpointer addr) +ftnptr_to_imethod (gpointer addr, gboolean *need_unbox) { InterpMethod *imethod; @@ -1690,44 +1690,59 @@ ftnptr_to_imethod (gpointer addr) g_assert (ftndesc); g_assert (ftndesc->method); - imethod = ftndesc->interp_method; - if (!imethod) { + if (!ftndesc->interp_method) { imethod = mono_interp_get_imethod (ftndesc->method, error); mono_error_assert_ok (error); mono_memory_barrier (); + // FIXME Handle unboxing here ? ftndesc->interp_method = imethod; } + *need_unbox = INTERP_IMETHOD_IS_TAGGED_UNBOX (ftndesc->interp_method); + imethod = INTERP_IMETHOD_UNTAG_UNBOX (ftndesc->interp_method); } else { /* Function pointers are represented by their InterpMethod */ - imethod = (InterpMethod*)addr; + *need_unbox = INTERP_IMETHOD_IS_TAGGED_UNBOX (addr); + imethod = INTERP_IMETHOD_UNTAG_UNBOX (addr); } return imethod; } static gpointer -imethod_to_ftnptr (InterpMethod *imethod) +imethod_to_ftnptr (InterpMethod *imethod, gboolean need_unbox) { if (mono_llvm_only) { ERROR_DECL (error); /* Function pointers are represented by a MonoFtnDesc structure */ - MonoFtnDesc *ftndesc = imethod->ftndesc; - if (!ftndesc) { - ftndesc = mini_llvmonly_load_method_ftndesc (imethod->method, FALSE, FALSE, error); + MonoFtnDesc **ftndesc_p; + if (need_unbox) + ftndesc_p = &imethod->ftndesc_unbox; + else + ftndesc_p = &imethod->ftndesc; + if (!*ftndesc_p) { + MonoFtnDesc *ftndesc = mini_llvmonly_load_method_ftndesc (imethod->method, FALSE, need_unbox, error); mono_error_assert_ok (error); + if (need_unbox) + ftndesc->interp_method = INTERP_IMETHOD_TAG_UNBOX (imethod); + else + ftndesc->interp_method = imethod; mono_memory_barrier (); - imethod->ftndesc = ftndesc; + *ftndesc_p = ftndesc; } - return ftndesc; + return *ftndesc_p; } else { - return imethod; + if (need_unbox) + return INTERP_IMETHOD_TAG_UNBOX (imethod); + else + return imethod; } } static void interp_delegate_ctor (MonoObjectHandle this_obj, MonoObjectHandle target, gpointer addr, MonoError *error) { + gboolean need_unbox; /* addr is the result of an LDFTN opcode */ - InterpMethod *imethod = ftnptr_to_imethod (addr); + InterpMethod *imethod = ftnptr_to_imethod (addr, &need_unbox); if (!(imethod->method->flags & METHOD_ATTRIBUTE_STATIC)) { MonoMethod *invoke = mono_get_delegate_invoke_internal (mono_handle_class (this_obj)); @@ -3484,12 +3499,10 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs goto call; } MINT_IN_CASE(MINT_CALLI) { - MonoMethodSignature *csignature; - - csignature = (MonoMethodSignature*)frame->imethod->data_items [ip [4]]; + gboolean need_unbox; /* In mixed mode, stay in the interpreter for simplicity even if there is an AOT version of the callee */ - cmethod = ftnptr_to_imethod (LOCAL_VAR (ip [2], gpointer)); + cmethod = ftnptr_to_imethod (LOCAL_VAR (ip [2], gpointer), &need_unbox); if (cmethod->method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) { cmethod = mono_interp_get_imethod (mono_marshal_get_native_wrapper (cmethod->method, FALSE, FALSE), error); @@ -3499,15 +3512,11 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs return_offset = ip [1]; call_args_offset = ip [3]; - if (csignature->hasthis) { + if (need_unbox) { MonoObject *this_arg = LOCAL_VAR (call_args_offset, MonoObject*); - - if (m_class_is_valuetype (this_arg->vtable->klass)) { - gpointer unboxed = mono_object_unbox_internal (this_arg); - LOCAL_VAR (call_args_offset, gpointer) = unboxed; - } + LOCAL_VAR (call_args_offset, gpointer) = mono_object_unbox_internal (this_arg); } - ip += 5; + ip += 4; goto call; } @@ -6508,17 +6517,18 @@ MINT_IN_CASE(MINT_BRTRUE_I8_SP) ZEROP_SP(gint64, !=); MINT_IN_BREAK; MINT_IN_CASE(MINT_LDFTN) { InterpMethod *m = (InterpMethod*)frame->imethod->data_items [ip [2]]; - LOCAL_VAR (ip [1], gpointer) = imethod_to_ftnptr (m); + LOCAL_VAR (ip [1], gpointer) = imethod_to_ftnptr (m, FALSE); ip += 3; MINT_IN_BREAK; } MINT_IN_CASE(MINT_LDVIRTFTN) { - InterpMethod *m = (InterpMethod*)frame->imethod->data_items [ip [3]]; + InterpMethod *virtual_method = (InterpMethod*)frame->imethod->data_items [ip [3]]; MonoObject *o = LOCAL_VAR (ip [2], MonoObject*); NULL_CHECK (o); - m = get_virtual_method (m, o->vtable); - LOCAL_VAR (ip [1], gpointer) = imethod_to_ftnptr (m); + InterpMethod *res_method = get_virtual_method (virtual_method, o->vtable); + gboolean need_unbox = m_class_is_valuetype (res_method->method->klass) && !m_class_is_valuetype (virtual_method->method->klass); + LOCAL_VAR (ip [1], gpointer) = imethod_to_ftnptr (res_method, need_unbox); ip += 4; MINT_IN_BREAK; } @@ -6529,7 +6539,7 @@ MINT_IN_CASE(MINT_BRTRUE_I8_SP) ZEROP_SP(gint64, !=); MINT_IN_BREAK; InterpMethod *m = mono_interp_get_imethod (cmethod, error); mono_error_assert_ok (error); - LOCAL_VAR (ip [1], gpointer) = imethod_to_ftnptr (m); + LOCAL_VAR (ip [1], gpointer) = imethod_to_ftnptr (m, FALSE); ip += 3; MINT_IN_BREAK; } @@ -6711,7 +6721,7 @@ MINT_IN_CASE(MINT_BRTRUE_I8_SP) ZEROP_SP(gint64, !=); MINT_IN_BREAK; mono_error_assert_ok (error); } g_assert (del->interp_method); - LOCAL_VAR (ip [1], gpointer) = imethod_to_ftnptr (del->interp_method); + LOCAL_VAR (ip [1], gpointer) = imethod_to_ftnptr (del->interp_method, FALSE); ip += 3; MINT_IN_BREAK; } diff --git a/src/mono/mono/mini/interp/mintops.def b/src/mono/mono/mini/interp/mintops.def index 9ef942e9d1b25..a274ff2c1b4ad 100644 --- a/src/mono/mono/mini/interp/mintops.def +++ b/src/mono/mono/mini/interp/mintops.def @@ -664,7 +664,7 @@ OPDEF(MINT_CALL, "call", 4, 1, 1, MintOpMethodToken) OPDEF(MINT_CALLVIRT, "callvirt", 4, 1, 1, MintOpMethodToken) OPDEF(MINT_CALLVIRT_FAST, "callvirt.fast", 5, 1, 1, MintOpMethodToken) OPDEF(MINT_CALL_DELEGATE, "call.delegate", 5, 1, 1, MintOpTwoShorts) -OPDEF(MINT_CALLI, "calli", 5, 1, 2, MintOpMethodToken) +OPDEF(MINT_CALLI, "calli", 4, 1, 2, MintOpNoArgs) OPDEF(MINT_CALLI_NAT, "calli.nat", 8, 1, 2, MintOpMethodToken) OPDEF(MINT_CALLI_NAT_DYNAMIC, "calli.nat.dynamic", 5, 1, 2, MintOpMethodToken) OPDEF(MINT_CALLI_NAT_FAST, "calli.nat.fast", 7, 1, 2, MintOpMethodToken) diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 0e71341d852f5..0196a402d6d0c 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -3511,7 +3511,6 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target interp_add_ins (td, MINT_CALLI); interp_ins_set_dreg (td->last_ins, dreg); interp_ins_set_sregs2 (td->last_ins, fp_sreg, MINT_CALL_ARGS_SREG); - td->last_ins->data [0] = get_data_item_index (td, (void *)csignature); } } else { InterpMethod *imethod = mono_interp_get_imethod (target_method, error); diff --git a/src/mono/mono/mini/mini.h b/src/mono/mono/mini/mini.h index c4bd7def1cbcb..c7ad6d4c5fa76 100644 --- a/src/mono/mono/mini/mini.h +++ b/src/mono/mono/mini/mini.h @@ -1144,7 +1144,7 @@ typedef struct gpointer addr; gpointer arg; MonoMethod *method; - /* InterpMethod* */ + /* Tagged InterpMethod* */ gpointer interp_method; } MonoFtnDesc; diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 7b090ae247847..320be9692e23c 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -1556,9 +1556,6 @@ https://github.com/dotnet/runtime/issues/54396 - - https://github.com/dotnet/runtime/issues/54374 - https://github.com/dotnet/runtime/issues/54381 From 93817770ab31e50d0f9b79ffacc02041047327bf Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Sat, 9 Oct 2021 23:07:35 +0300 Subject: [PATCH 025/106] Add ISMETHOD and ISMETHODHASH macros (#59993) Co-authored-by: Andy Ayers --- src/coreclr/jit/gentree.h | 10 ++++------ src/coreclr/jit/jit.h | 3 +++ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 52844499e134b..7fc867aef130a 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -4774,12 +4774,10 @@ struct GenTreeCall final : public GenTree void ResetArgInfo(); - GenTreeCallFlags gtCallMoreFlags; // in addition to gtFlags - - unsigned char gtCallType : 3; // value from the gtCallTypes enumeration - unsigned char gtReturnType : 5; // exact return type - - CORINFO_CLASS_HANDLE gtRetClsHnd; // The return type handle of the call if it is a struct; always available + GenTreeCallFlags gtCallMoreFlags; // in addition to gtFlags + gtCallTypes gtCallType : 3; // value from the gtCallTypes enumeration + var_types gtReturnType : 5; // exact return type + CORINFO_CLASS_HANDLE gtRetClsHnd; // The return type handle of the call if it is a struct; always available union { // only used for CALLI unmanaged calls (CT_INDIRECT) diff --git a/src/coreclr/jit/jit.h b/src/coreclr/jit/jit.h index e212226851c7c..218871e154fc5 100644 --- a/src/coreclr/jit/jit.h +++ b/src/coreclr/jit/jit.h @@ -529,6 +529,9 @@ const bool dspGCtbls = true; if (JitTls::GetCompiler()->verbose) \ JitTls::GetCompiler()->fgTableDispBasicBlock(b); #define VERBOSE JitTls::GetCompiler()->verbose +// Development-time only macros, simplify guards for specified IL methods one wants to debug/add log messages for +#define ISMETHOD(name) (strcmp(JitTls::GetCompiler()->impInlineRoot()->info.compMethodName, name) == 0) +#define ISMETHODHASH(hash) (JitTls::GetCompiler()->impInlineRoot()->info.compMethodHash() == hash) #else // !DEBUG #define JITDUMP(...) #define JITDUMPEXEC(x) From 2e8615bd19cacaea6349f677787f9cd7d6a6c9e1 Mon Sep 17 00:00:00 2001 From: SingleAccretion <62474226+SingleAccretion@users.noreply.github.com> Date: Sun, 10 Oct 2021 01:58:25 +0300 Subject: [PATCH 026/106] Fix a build break (#60222) The other casts around gtReturnType should be cleaned up as well, but that is left for another day. --- src/coreclr/jit/morph.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 64b98336125ec..56bf74801c245 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -64,7 +64,7 @@ GenTree* Compiler::fgMorphIntoHelperCall(GenTree* tree, int helper, GenTreeCall: GenTreeCall* call = tree->AsCall(); call->gtCallType = CT_HELPER; - call->gtReturnType = static_cast(tree->TypeGet()); + call->gtReturnType = tree->TypeGet(); call->gtCallMethHnd = eeFindHelper(helper); call->gtCallThisArg = nullptr; call->gtCallArgs = args; From 6c09f34947026ed988fd3013fdaa624a5fab0f26 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Sun, 10 Oct 2021 16:09:37 +0200 Subject: [PATCH 027/106] [mono][interp] Avoid calls to m_class_get_mem_manager () in get_virtual_method_fast () (#60229) unless needed. --- src/mono/mono/mini/interp/interp.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index 7ad368990b162..6a0039ca872d7 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -694,11 +694,12 @@ static InterpMethod* // Inlining causes additional stack use in caller. get_virtual_method_fast (InterpMethod *imethod, MonoVTable *vtable, int offset) { gpointer *table; - MonoMemoryManager *memory_manager = m_class_get_mem_manager (vtable->klass); + MonoMemoryManager *memory_manager = NULL; table = get_method_table (vtable, offset); - if (!table) { + if (G_UNLIKELY (!table)) { + memory_manager = m_class_get_mem_manager (vtable->klass); /* Lazily allocate method table */ mono_mem_manager_lock (memory_manager); table = get_method_table (vtable, offset); @@ -707,8 +708,10 @@ get_virtual_method_fast (InterpMethod *imethod, MonoVTable *vtable, int offset) mono_mem_manager_unlock (memory_manager); } - if (!table [offset]) { + if (G_UNLIKELY (!table [offset])) { InterpMethod *target_imethod = get_virtual_method (imethod, vtable); + if (!memory_manager) + memory_manager = m_class_get_mem_manager (vtable->klass); /* Lazily initialize the method table slot */ mono_mem_manager_lock (memory_manager); if (!table [offset]) { @@ -727,8 +730,10 @@ get_virtual_method_fast (InterpMethod *imethod, MonoVTable *vtable, int offset) /* Virtual generic or interface call. Multiple methods in slot */ InterpMethod *target_imethod = get_target_imethod ((GSList*)table [offset], imethod); - if (!target_imethod) { + if (G_UNLIKELY (!target_imethod)) { target_imethod = get_virtual_method (imethod, vtable); + if (!memory_manager) + memory_manager = m_class_get_mem_manager (vtable->klass); mono_mem_manager_lock (memory_manager); if (!get_target_imethod ((GSList*)table [offset], imethod)) table [offset] = append_imethod (memory_manager, (GSList*)table [offset], imethod, target_imethod); From 4a42b37d03c24a6b49be78086cf712011409af24 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Mon, 11 Oct 2021 09:15:24 +0200 Subject: [PATCH 028/106] fallocate sets errno on error, posix_fallocate did not (#60185) --- src/libraries/Native/Unix/System.Native/pal_io.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/Native/Unix/System.Native/pal_io.c b/src/libraries/Native/Unix/System.Native/pal_io.c index 9d313dd6e9951..49aaeca6c2078 100644 --- a/src/libraries/Native/Unix/System.Native/pal_io.c +++ b/src/libraries/Native/Unix/System.Native/pal_io.c @@ -1015,7 +1015,7 @@ int32_t SystemNative_FAllocate(intptr_t fd, int64_t offset, int64_t length) int fileDescriptor = ToFileDescriptor(fd); int32_t result; #if HAVE_FALLOCATE // Linux - while ((result = fallocate(fileDescriptor, FALLOC_FL_KEEP_SIZE, (off_t)offset, (off_t)length)) == EINTR); + while ((result = fallocate(fileDescriptor, FALLOC_FL_KEEP_SIZE, (off_t)offset, (off_t)length)) == -1 && errno == EINTR); #elif defined(F_PREALLOCATE) // macOS fstore_t fstore; fstore.fst_flags = F_ALLOCATEALL; // Allocate all requested space or no space at all. From 565ff522bf630ff0556f3dc590dcb7337696a5d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marie=20P=C3=ADchov=C3=A1?= <11718369+ManickaP@users.noreply.github.com> Date: Mon, 11 Oct 2021 11:01:10 +0200 Subject: [PATCH 029/106] Fixed exception type for ConnectionShutdownInitiatedByTransport (#60181) --- .../Net/Quic/Implementations/MsQuic/MsQuicConnection.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs index 60abf523ef0a2..66e594c34fca5 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs @@ -283,6 +283,11 @@ private static uint HandleEventShutdownInitiatedByTransport(State state, ref Con state.ConnectTcs = null; } + // To throw QuicConnectionAbortedException (instead of QuicOperationAbortedException) out of AcceptStreamAsync() since + // it wasn't our side who shutdown the connection. + // We should rather keep the Status and propagate it either in a different exception or as a different field of QuicConnectionAbortedException. + // See: https://github.com/dotnet/runtime/issues/60133 + state.AbortErrorCode = 0; state.AcceptQueue.Writer.TryComplete(); return MsQuicStatusCodes.Success; } From 5f9fec9b3af9c3c7de3d00b56778e3b5af422812 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Mon, 11 Oct 2021 09:59:07 -0400 Subject: [PATCH 030/106] Fix test sensitivity to PKCS7 certificate ordering (#59818) Android exports PKCS7 certificates in a different order, and the xunit assertion is sensitive to order. So we sort them first. Fixes #59777. --- .../tests/CollectionTests.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs index da647d19ad5ef..2c77252705f01 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs @@ -1745,7 +1745,9 @@ static void AssertPem(X509Certificate2Collection expected, ReadOnlySpan pe using (ImportedCollection imported = Cert.Import(data)) { - Assert.Equal(expected.ToArray(), imported.Collection.ToArray(), new X509Certificate2EqualityComparer()); + X509Certificate2[] expectedCollection = expected.OrderBy(c => c.Thumbprint).ToArray(); + X509Certificate2[] actualCollection = imported.Collection.OrderBy(c => c.Thumbprint).ToArray(); + Assert.Equal(expectedCollection, actualCollection, new X509Certificate2EqualityComparer()); } } From 9dcde5f8a182a9202a50afa92fec463f6654ecd9 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Mon, 11 Oct 2021 09:59:38 -0400 Subject: [PATCH 031/106] Remove assert preventing empty PKCS7 exports (#59812) An empty PKCS7 collection is valid, so don't require the certHandles count to be greater than 0. Before: ``` DOTNET : ((null) warning) Process terminated due to " at System.Diagnostics.DebugProvider.Fail(String message, String detailMessage) DOTNET : at System.Diagnostics.Debug.Fail(String message, String detailMessage) DOTNET : at System.Diagnostics.Debug.Assert(Boolean condition, String message, String detailMessage) DOTNET : at System.Diagnostics.Debug.Assert(Boolean condition) ``` After: ``` [PASS] System.Security.Cryptography.X509Certificates.Tests.CollectionTests.ExportPkcs7_Empty ``` --- .../src/Internal/Cryptography/Pal.Android/StorePal.ExportPal.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Android/StorePal.ExportPal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Android/StorePal.ExportPal.cs index ef28cdf69f8e2..b98ed82966316 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Android/StorePal.ExportPal.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Android/StorePal.ExportPal.cs @@ -39,7 +39,6 @@ protected override byte[] ExportPkcs7() } } - Debug.Assert(certHandles.Length > 0); return Interop.AndroidCrypto.X509ExportPkcs7(certHandles); } From 5aad989cebe00f0987fcb842ea5b7cbe986c67df Mon Sep 17 00:00:00 2001 From: Badre BSAILA <54767641+pedrobsaila@users.noreply.github.com> Date: Mon, 11 Oct 2021 16:44:43 +0200 Subject: [PATCH 032/106] make System.Security.Cryptography.Pkcs conform to interop guidelines (#59913) --- .../Interop.CertDuplicateCertificateContext_IntPtr.cs} | 0 .../src/System.Security.Cryptography.Pkcs.csproj | 5 +++-- 2 files changed, 3 insertions(+), 2 deletions(-) rename src/libraries/{System.Security.Cryptography.Pkcs/src/Interop/Windows/Crypt32/Interop.CertDuplicateCertificateContext.cs => Common/src/Interop/Windows/Crypt32/Interop.CertDuplicateCertificateContext_IntPtr.cs} (100%) diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/Interop/Windows/Crypt32/Interop.CertDuplicateCertificateContext.cs b/src/libraries/Common/src/Interop/Windows/Crypt32/Interop.CertDuplicateCertificateContext_IntPtr.cs similarity index 100% rename from src/libraries/System.Security.Cryptography.Pkcs/src/Interop/Windows/Crypt32/Interop.CertDuplicateCertificateContext.cs rename to src/libraries/Common/src/Interop/Windows/Crypt32/Interop.CertDuplicateCertificateContext_IntPtr.cs diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System.Security.Cryptography.Pkcs.csproj b/src/libraries/System.Security.Cryptography.Pkcs/src/System.Security.Cryptography.Pkcs.csproj index 0abd0890f6e1e..f91940deada37 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System.Security.Cryptography.Pkcs.csproj +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System.Security.Cryptography.Pkcs.csproj @@ -1,4 +1,4 @@ - + true true @@ -159,7 +159,6 @@ System.Security.Cryptography.Pkcs.EnvelopedCms - @@ -193,6 +192,8 @@ System.Security.Cryptography.Pkcs.EnvelopedCms Link="Common\Interop\Windows\Crypt32\Interop.CertContextPropId.cs" /> + Date: Mon, 11 Oct 2021 17:42:23 +0200 Subject: [PATCH 033/106] Reenable ExportCertificatePems_MultiCert test on Android (#60251) The issue was fixed. --- .../tests/CollectionTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs index 2c77252705f01..ac92a40e6d947 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs @@ -1583,7 +1583,6 @@ public static void ExportCertificatePems_SingleCert() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/59777", TestPlatforms.Android)] public static void ExportCertificatePems_MultiCert() { const string MultiPem = "-----BEGIN CERTIFICATE-----\n" + From 5f3062d81a5caa444308937539ad136b72218c29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Mon, 11 Oct 2021 18:58:02 +0200 Subject: [PATCH 034/106] Add runtime-community.yml pipeline and add s390x job (#60255) --- eng/pipelines/common/platform-matrix.yml | 26 ++++++++ .../libraries/helix-queues-setup.yml | 4 ++ eng/pipelines/runtime-community.yml | 59 +++++++++++++++++++ src/libraries/tests.proj | 4 ++ 4 files changed, 93 insertions(+) create mode 100644 eng/pipelines/runtime-community.yml diff --git a/eng/pipelines/common/platform-matrix.yml b/eng/pipelines/common/platform-matrix.yml index 3672691bbb05a..efa204878083a 100644 --- a/eng/pipelines/common/platform-matrix.yml +++ b/eng/pipelines/common/platform-matrix.yml @@ -209,6 +209,32 @@ jobs: ${{ insert }}: ${{ parameters.jobParameters }} buildingOnSourceBuildImage: true +# Linux s390x + +- ${{ if or(containsValue(parameters.platforms, 'Linux_s390x'), in(parameters.platformGroup, 'all', 'gcstress')) }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: Linux + archType: s390x + targetRid: linux-s390x + platform: Linux_s390x + container: + image: ubuntu-18.04-cross-s390x-20201102145728-d6e0352 + registry: mcr + jobParameters: + runtimeFlavor: ${{ parameters.runtimeFlavor }} + stagedBuild: ${{ parameters.stagedBuild }} + buildConfig: ${{ parameters.buildConfig }} + ${{ if eq(parameters.passPlatforms, true) }}: + platforms: ${{ parameters.platforms }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + crossBuild: true + crossrootfsDir: '/crossrootfs/s390x' + ${{ insert }}: ${{ parameters.jobParameters }} + # WebAssembly - ${{ if containsValue(parameters.platforms, 'Browser_wasm') }}: diff --git a/eng/pipelines/libraries/helix-queues-setup.yml b/eng/pipelines/libraries/helix-queues-setup.yml index 5a82951839ff4..73432c9c408b1 100644 --- a/eng/pipelines/libraries/helix-queues-setup.yml +++ b/eng/pipelines/libraries/helix-queues-setup.yml @@ -82,6 +82,10 @@ jobs: # Limiting interp runs as we don't need as much coverage. - (Debian.10.Amd64.Open)Ubuntu.1804.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:debian-10-helix-amd64-20210304164434-56c6673 + # Linux s390x + - ${{ if eq(parameters.platform, 'Linux_s390x') }}: + - Ubuntu.2004.S390X.Experimental.Open + # OSX arm64 - ${{ if eq(parameters.platform, 'OSX_arm64') }}: - OSX.1100.ARM64.Open diff --git a/eng/pipelines/runtime-community.yml b/eng/pipelines/runtime-community.yml new file mode 100644 index 0000000000000..5bebcc00a2663 --- /dev/null +++ b/eng/pipelines/runtime-community.yml @@ -0,0 +1,59 @@ +trigger: none + +schedules: + - cron: "0 7,19 * * *" # run at 7:00 and 19:00 (UTC) which is 23:00 and 11:00 (PST). + displayName: Runtime-community default schedule + branches: + include: + - main + always: false # run only if there were changes since the last successful scheduled run. + +variables: + - template: /eng/pipelines/common/variables.yml + +jobs: +# +# Evaluate paths +# +- ${{ if eq(variables.dependOnEvaluatePaths, true) }}: + - template: /eng/pipelines/common/evaluate-default-paths.yml + +# +# s390x +# 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: + - Linux_s390x + 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 + buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=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) + condition: >- + or( + eq(variables['librariesContainsChange'], true), + eq(variables['monoContainsChange'], true), + eq(variables['isFullMatrix'], true)) diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index b3a8575c04736..2e1ffe3b100e9 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -258,6 +258,10 @@ + + + + From bda872709d797a0149349a8d4531774921e7a99b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksey=20Kliger=20=28=CE=BBgeek=29?= Date: Mon, 11 Oct 2021 13:25:38 -0400 Subject: [PATCH 035/106] [reflection] Return n2m wrapper from RuntimeMethodHandle.GetFunctionPointer (#60200) for functions marked with UnmanagedCallersOnlyAttribute, return a pointer to the n2m wrapper for calls to GetFunctionPointer Contributes to https://github.com/dotnet/runtime/issues/59815 --- src/mono/mono/metadata/icall.c | 5 +++++ src/mono/mono/mini/interp/interp.c | 13 ++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/mono/mono/metadata/icall.c b/src/mono/mono/metadata/icall.c index c48f2b1199a0d..04479684a6125 100644 --- a/src/mono/mono/metadata/icall.c +++ b/src/mono/mono/metadata/icall.c @@ -6293,6 +6293,11 @@ ves_icall_System_Environment_get_TickCount64 (void) gpointer ves_icall_RuntimeMethodHandle_GetFunctionPointer (MonoMethod *method, MonoError *error) { + /* WISH: we should do this in managed */ + if (G_UNLIKELY (mono_method_has_unmanaged_callers_only_attribute (method))) { + method = mono_marshal_get_managed_wrapper (method, NULL, (MonoGCHandle)0, error); + return_val_if_nok (error, NULL); + } return mono_get_runtime_callbacks ()->get_ftnptr (method, error); } diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index 6a0039ca872d7..be586a546ed86 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -6542,9 +6542,16 @@ MINT_IN_CASE(MINT_BRTRUE_I8_SP) ZEROP_SP(gint64, !=); MINT_IN_BREAK; MonoMethod *cmethod = LOCAL_VAR (ip [2], MonoMethod*); - InterpMethod *m = mono_interp_get_imethod (cmethod, error); - mono_error_assert_ok (error); - LOCAL_VAR (ip [1], gpointer) = imethod_to_ftnptr (m, FALSE); + if (G_UNLIKELY (mono_method_has_unmanaged_callers_only_attribute (cmethod))) { + cmethod = mono_marshal_get_managed_wrapper (cmethod, NULL, (MonoGCHandle)0, error); + mono_error_assert_ok (error); + gpointer addr = mini_get_interp_callbacks ()->create_method_pointer (cmethod, TRUE, error); + LOCAL_VAR (ip [1], gpointer) = addr; + } else { + InterpMethod *m = mono_interp_get_imethod (cmethod, error); + mono_error_assert_ok (error); + LOCAL_VAR (ip [1], gpointer) = imethod_to_ftnptr (m, FALSE); + } ip += 3; MINT_IN_BREAK; } From 7088332d24a276f557e1fe3612947fb17e0f04a3 Mon Sep 17 00:00:00 2001 From: Vladimir Sadov Date: Mon, 11 Oct 2021 13:52:28 -0700 Subject: [PATCH 036/106] Some more cleanups in Assembly/Binder/Loader area (#59590) * moved default binder allocation to AppDomain::Create * allowNativeSkip parameter is unused. * Removed PEFile::IsResource and PEFile::IsIStream - these do not exist in CoreClr * make GCC happy * folded impl * Folded PEFile into PEAssembly * renamed pefile files * reorder PEAssembly members * removed meaningless LAYOUT_CREATEIFNEEDED - we always require that * r2r bundled image should Open via cache - no reasons not to. * Some cleanup in PEImage * PEAssembly needs only one reference to PEImage * a few tweaks * some more * We never update published hosted assemblies, no need to support the scenario. * Done with PEAssembly for now * couple fixes, mostly in comments afected by renames * more cleanups for PEImage * PR review suggestion Co-authored-by: Elinor Fung * PR review feedback * this line should be deleted, not commented. * Apply suggestions from code review Co-authored-by: Elinor Fung * More PR feedback Co-authored-by: Elinor Fung --- .../src/System/Reflection/RuntimeModule.cs | 6 +- src/coreclr/binder/bindertracing.cpp | 8 +- src/coreclr/debug/createdump/crashinfo.cpp | 2 +- src/coreclr/debug/daccess/daccess.cpp | 57 +- src/coreclr/debug/daccess/dacdbiimpl.cpp | 74 +-- src/coreclr/debug/daccess/dacdbiimpl.h | 16 +- src/coreclr/debug/daccess/dacfn.cpp | 4 +- src/coreclr/debug/daccess/dacimpl.h | 16 +- src/coreclr/debug/daccess/enummem.cpp | 26 +- src/coreclr/debug/daccess/request.cpp | 37 +- src/coreclr/debug/daccess/task.cpp | 60 +- src/coreclr/debug/di/module.cpp | 8 +- src/coreclr/debug/di/process.cpp | 18 +- src/coreclr/debug/di/rspriv.h | 10 +- src/coreclr/debug/ee/debugger.cpp | 17 +- src/coreclr/debug/inc/dacdbiinterface.h | 20 +- src/coreclr/debug/inc/dacdbistructures.h | 6 +- src/coreclr/debug/inc/dbgipcevents.h | 2 +- src/coreclr/inc/corhdr.h | 2 +- src/coreclr/inc/daccess.h | 2 +- src/coreclr/inc/dacprivate.h | 6 +- src/coreclr/inc/vptr_list.h | 1 - src/coreclr/vm/CMakeLists.txt | 6 +- src/coreclr/vm/appdomain.cpp | 247 ++----- src/coreclr/vm/appdomain.hpp | 49 +- src/coreclr/vm/assembly.cpp | 99 ++- src/coreclr/vm/assembly.hpp | 14 +- src/coreclr/vm/assemblybinder.cpp | 2 +- src/coreclr/vm/assemblyname.cpp | 2 +- src/coreclr/vm/assemblynative.cpp | 42 +- src/coreclr/vm/assemblynative.hpp | 2 +- src/coreclr/vm/assemblyspec.cpp | 30 +- src/coreclr/vm/assemblyspec.hpp | 24 +- src/coreclr/vm/ceeload.cpp | 312 ++++----- src/coreclr/vm/ceeload.h | 102 +-- src/coreclr/vm/ceemain.cpp | 1 - src/coreclr/vm/clrex.cpp | 7 +- src/coreclr/vm/clrex.h | 3 +- src/coreclr/vm/clsload.cpp | 24 +- src/coreclr/vm/commodule.cpp | 45 +- src/coreclr/vm/commodule.h | 2 - src/coreclr/vm/common.h | 4 +- src/coreclr/vm/coreassemblyspec.cpp | 4 +- src/coreclr/vm/corhost.cpp | 2 - src/coreclr/vm/debugdebugger.cpp | 10 +- src/coreclr/vm/domainfile.cpp | 62 +- src/coreclr/vm/domainfile.h | 43 +- src/coreclr/vm/domainfile.inl | 31 +- src/coreclr/vm/dwbucketmanager.hpp | 10 +- src/coreclr/vm/ecalllist.h | 1 - src/coreclr/vm/eedbginterfaceimpl.h | 2 +- src/coreclr/vm/encee.cpp | 6 +- src/coreclr/vm/encee.h | 6 +- src/coreclr/vm/eventtrace.cpp | 10 +- src/coreclr/vm/exceptmacros.h | 2 +- src/coreclr/vm/inlinetracking.cpp | 4 +- src/coreclr/vm/jitinterface.cpp | 10 +- src/coreclr/vm/loaderallocator.cpp | 4 +- src/coreclr/vm/memberload.cpp | 2 +- src/coreclr/vm/methoditer.cpp | 3 - src/coreclr/vm/methodtablebuilder.cpp | 7 +- src/coreclr/vm/multicorejit.cpp | 21 +- src/coreclr/vm/multicorejitplayer.cpp | 11 +- src/coreclr/vm/nativeimage.cpp | 5 +- src/coreclr/vm/nativeimage.h | 2 +- src/coreclr/vm/nativelibrary.cpp | 2 +- src/coreclr/vm/{pefile.cpp => peassembly.cpp} | 619 +++++------------- src/coreclr/vm/{pefile.h => peassembly.h} | 483 +++++--------- src/coreclr/vm/{pefile.inl => peassembly.inl} | 487 +++----------- src/coreclr/vm/peimage.cpp | 251 +++---- src/coreclr/vm/peimage.h | 285 ++++---- src/coreclr/vm/peimage.inl | 247 ++----- src/coreclr/vm/peimagelayout.cpp | 2 +- src/coreclr/vm/perfinfo.cpp | 10 +- src/coreclr/vm/perfinfo.h | 2 +- src/coreclr/vm/perfmap.cpp | 20 +- src/coreclr/vm/perfmap.h | 6 +- src/coreclr/vm/proftoeeinterfaceimpl.cpp | 47 +- src/coreclr/vm/readytoruninfo.cpp | 14 +- src/coreclr/vm/runtimehandles.cpp | 14 +- src/coreclr/vm/siginfo.cpp | 2 +- src/coreclr/vm/typeparse.cpp | 4 +- 82 files changed, 1384 insertions(+), 2784 deletions(-) rename src/coreclr/vm/{pefile.cpp => peassembly.cpp} (67%) rename src/coreclr/vm/{pefile.h => peassembly.h} (57%) rename src/coreclr/vm/{pefile.inl => peassembly.inl} (56%) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeModule.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeModule.cs index 74849de7d1ed8..fda6459b2551d 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeModule.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeModule.cs @@ -30,9 +30,6 @@ internal RuntimeType[] GetDefinedTypes() { return GetTypes(this); } - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern bool IsResource(RuntimeModule module); #endregion #region Module overrides @@ -472,7 +469,8 @@ public override Guid ModuleVersionId public override bool IsResource() { - return IsResource(this); + // CoreClr does not support resource-only modules. + return false; } [RequiresUnreferencedCode("Fields might be removed")] diff --git a/src/coreclr/binder/bindertracing.cpp b/src/coreclr/binder/bindertracing.cpp index b03eb2f66db5f..499e20d928887 100644 --- a/src/coreclr/binder/bindertracing.cpp +++ b/src/coreclr/binder/bindertracing.cpp @@ -147,12 +147,12 @@ namespace DomainAssembly *parentAssembly = spec->GetParentAssembly(); if (parentAssembly != nullptr) { - PEAssembly *peAssembly = parentAssembly->GetFile(); - _ASSERTE(peAssembly != nullptr); - peAssembly->GetDisplayName(request.RequestingAssembly); + PEAssembly *pPEAssembly = parentAssembly->GetPEAssembly(); + _ASSERTE(pPEAssembly != nullptr); + pPEAssembly->GetDisplayName(request.RequestingAssembly); AppDomain *domain = parentAssembly->GetAppDomain(); - AssemblyBinder *binder = peAssembly->GetAssemblyBinder(); + AssemblyBinder *binder = pPEAssembly->GetAssemblyBinder(); GetAssemblyLoadContextNameFromBinder(binder, domain, request.RequestingAssemblyLoadContext); } diff --git a/src/coreclr/debug/createdump/crashinfo.cpp b/src/coreclr/debug/createdump/crashinfo.cpp index 8ab327eb15578..72ad06c51b521 100644 --- a/src/coreclr/debug/createdump/crashinfo.cpp +++ b/src/coreclr/debug/createdump/crashinfo.cpp @@ -320,7 +320,7 @@ CrashInfo::EnumerateManagedModules(IXCLRDataProcess* pClrDataProcess) if (SUCCEEDED(hr = moduleData.Request(pClrDataModule.GetPtr()))) { TRACE("MODULE: %" PRIA PRIx64 " dyn %d inmem %d file %d pe %" PRIA PRIx64 " pdb %" PRIA PRIx64, (uint64_t)moduleData.LoadedPEAddress, moduleData.IsDynamic, - moduleData.IsInMemory, moduleData.IsFileLayout, (uint64_t)moduleData.PEFile, (uint64_t)moduleData.InMemoryPdbAddress); + moduleData.IsInMemory, moduleData.IsFileLayout, (uint64_t)moduleData.PEAssembly, (uint64_t)moduleData.InMemoryPdbAddress); if (!moduleData.IsDynamic && moduleData.LoadedPEAddress != 0) { diff --git a/src/coreclr/debug/daccess/daccess.cpp b/src/coreclr/debug/daccess/daccess.cpp index 0630cfa710a37..35b834542c91c 100644 --- a/src/coreclr/debug/daccess/daccess.cpp +++ b/src/coreclr/debug/daccess/daccess.cpp @@ -679,11 +679,6 @@ MetaEnum::New(Module* mod, *handle = TO_CDENUM(NULL); } - if (!mod->GetFile()->HasMetadata()) - { - return S_FALSE; - } - metaEnum = new (nothrow) MetaEnum; if (!metaEnum) { @@ -4079,9 +4074,9 @@ ClrDataAccess::GetModuleByAddress( { TADDR base; ULONG32 length; - PEFile* file = modDef->GetFile(); + PEAssembly* pPEAssembly = modDef->GetPEAssembly(); - if ((base = PTR_TO_TADDR(file->GetLoadedImageContents(&length)))) + if ((base = PTR_TO_TADDR(pPEAssembly->GetLoadedImageContents(&length)))) { if (TO_CDADDR(base) <= address && TO_CDADDR(base + length) > address) @@ -4133,9 +4128,9 @@ ClrDataAccess::StartEnumMethodDefinitionsByAddress( { TADDR base; ULONG32 length; - PEFile* file = modDef->GetFile(); + PEAssembly* assembly = modDef->GetPEAssembly(); - if ((base = PTR_TO_TADDR(file->GetLoadedImageContents(&length)))) + if ((base = PTR_TO_TADDR(assembly->GetLoadedImageContents(&length)))) { if (TO_CDADDR(base) <= address && TO_CDADDR(base + length) > address) @@ -6459,7 +6454,7 @@ ClrDataAccess::GetHostGcNotificationTable() } /* static */ bool -ClrDataAccess::GetMetaDataFileInfoFromPEFile(PEFile *pPEFile, +ClrDataAccess::GetMetaDataFileInfoFromPEFile(PEAssembly *pPEAssembly, DWORD &dwTimeStamp, DWORD &dwSize, DWORD &dwDataSize, @@ -6477,7 +6472,7 @@ ClrDataAccess::GetMetaDataFileInfoFromPEFile(PEFile *pPEFile, isNGEN = false; if (pDir == NULL || pDir->Size == 0) { - mdImage = pPEFile->GetILimage(); + mdImage = pPEAssembly->GetPEImage(); if (mdImage != NULL) { layout = mdImage->GetLoadedLayout(); @@ -6526,7 +6521,7 @@ ClrDataAccess::GetMetaDataFileInfoFromPEFile(PEFile *pPEFile, } /* static */ -bool ClrDataAccess::GetILImageInfoFromNgenPEFile(PEFile *peFile, +bool ClrDataAccess::GetILImageInfoFromNgenPEFile(PEAssembly *pPEAssembly, DWORD &dwTimeStamp, DWORD &dwSize, __out_ecount(cchFilePath) LPWSTR wszFilePath, @@ -6536,10 +6531,10 @@ bool ClrDataAccess::GetILImageInfoFromNgenPEFile(PEFile *peFile, DWORD dwWritten = 0; // use the IL File name - if (!peFile->GetPath().DacGetUnicode(cchFilePath, wszFilePath, (COUNT_T *)(&dwWritten))) + if (!pPEAssembly->GetPath().DacGetUnicode(cchFilePath, wszFilePath, (COUNT_T *)(&dwWritten))) { // Use DAC hint to retrieve the IL name. - peFile->GetModuleFileNameHint().DacGetUnicode(cchFilePath, wszFilePath, (COUNT_T *)(&dwWritten)); + pPEAssembly->GetModuleFileNameHint().DacGetUnicode(cchFilePath, wszFilePath, (COUNT_T *)(&dwWritten)); } dwTimeStamp = 0; dwSize = 0; @@ -6603,7 +6598,7 @@ bool ClrDataAccess::GetILImageNameFromNgenImage( LPCWSTR ilExtension, #endif // FEATURE_CORESYSTEM void * -ClrDataAccess::GetMetaDataFromHost(PEFile* peFile, +ClrDataAccess::GetMetaDataFromHost(PEAssembly* pPEAssembly, bool* isAlternate) { DWORD imageTimestamp, imageSize, dataSize; @@ -6634,7 +6629,7 @@ ClrDataAccess::GetMetaDataFromHost(PEFile* peFile, // so the field remains for now. if (!ClrDataAccess::GetMetaDataFileInfoFromPEFile( - peFile, + pPEAssembly, imageTimestamp, imageSize, dataSize, @@ -6647,7 +6642,7 @@ ClrDataAccess::GetMetaDataFromHost(PEFile* peFile, } // try direct match for the image that is loaded into the managed process - peFile->GetLoadedMetadata((COUNT_T *)(&dataSize)); + pPEAssembly->GetLoadedMetadata((COUNT_T *)(&dataSize)); DWORD allocSize = 0; if (!ClrSafeInt::addition(dataSize, sizeof(DAC_INSTANCE), allocSize)) @@ -6665,7 +6660,7 @@ ClrDataAccess::GetMetaDataFromHost(PEFile* peFile, buffer = (void*)(inst + 1); // APIs implemented by hosting debugger. It can use the path/filename, timestamp, and - // file size to find an exact match for the image. If that fails for an ngen'ed image, + // pPEAssembly size to find an exact match for the image. If that fails for an ngen'ed image, // we can request the IL image which it came from. if (m_legacyMetaDataLocator) { @@ -6701,7 +6696,7 @@ ClrDataAccess::GetMetaDataFromHost(PEFile* peFile, // isAlt = true; if (!ClrDataAccess::GetILImageInfoFromNgenPEFile( - peFile, + pPEAssembly, imageTimestamp, imageSize, uniPath, @@ -6781,13 +6776,13 @@ ClrDataAccess::GetMetaDataFromHost(PEFile* peFile, //++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // -// Given a PEFile or a ReflectionModule try to find the corresponding metadata +// Given a PEAssembly or a ReflectionModule try to find the corresponding metadata // We will first ask debugger to locate it. If fail, we will try // to get it from the target process // //++++++++++++++++++++++++++++++++++++++++++++++++++++++++ IMDInternalImport* -ClrDataAccess::GetMDImport(const PEFile* peFile, const ReflectionModule* reflectionModule, bool throwEx) +ClrDataAccess::GetMDImport(const PEAssembly* pPEAssembly, const ReflectionModule* reflectionModule, bool throwEx) { HRESULT status; PTR_CVOID mdBaseTarget = NULL; @@ -6796,22 +6791,22 @@ ClrDataAccess::GetMDImport(const PEFile* peFile, const ReflectionModule* reflect PVOID mdBaseHost = NULL; bool isAlternate = false; - _ASSERTE((peFile == NULL && reflectionModule != NULL) || (peFile != NULL && reflectionModule == NULL)); - TADDR peFileAddr = (peFile != NULL) ? dac_cast(peFile) : dac_cast(reflectionModule); + _ASSERTE((pPEAssembly == NULL && reflectionModule != NULL) || (pPEAssembly != NULL && reflectionModule == NULL)); + TADDR peAssemblyAddr = (pPEAssembly != NULL) ? dac_cast(pPEAssembly) : dac_cast(reflectionModule); // // Look for one we've already created. // - mdImport = m_mdImports.Get(peFileAddr); + mdImport = m_mdImports.Get(peAssemblyAddr); if (mdImport != NULL) { return mdImport; } - if (peFile != NULL) + if (pPEAssembly != NULL) { // Get the metadata size - mdBaseTarget = ((PEFile*)peFile)->GetLoadedMetadata(&mdSize); + mdBaseTarget = const_cast(pPEAssembly)->GetLoadedMetadata(&mdSize); } else if (reflectionModule != NULL) { @@ -6862,12 +6857,12 @@ ClrDataAccess::GetMDImport(const PEFile* peFile, const ReflectionModule* reflect } // Try to see if debugger can locate it - if (peFile != NULL && mdBaseHost == NULL && (m_target3 || m_legacyMetaDataLocator)) + if (pPEAssembly != NULL && mdBaseHost == NULL && (m_target3 || m_legacyMetaDataLocator)) { // We couldn't read the metadata from memory. Ask // the target for metadata as it may be able to // provide it from some alternate means. - mdBaseHost = GetMetaDataFromHost(const_cast(peFile), &isAlternate); + mdBaseHost = GetMetaDataFromHost(const_cast(pPEAssembly), &isAlternate); } if (mdBaseHost == NULL) @@ -6902,7 +6897,7 @@ ClrDataAccess::GetMDImport(const PEFile* peFile, const ReflectionModule* reflect // The m_mdImports list does get cleaned up by calls to ClrDataAccess::Flush, // i.e. every time the process changes state. - if (m_mdImports.Add(peFileAddr, mdImport, isAlternate) == NULL) + if (m_mdImports.Add(peAssemblyAddr, mdImport, isAlternate) == NULL) { mdImport->Release(); DacError(E_OUTOFMEMORY); @@ -6970,9 +6965,9 @@ HRESULT ClrDataAccess::VerifyDlls() } // Read the debug directory timestamp from the target mscorwks image using DAC - // Note that we don't use the PE timestamp because the PE file might be changed in ways + // Note that we don't use the PE timestamp because the PE pPEAssembly might be changed in ways // that don't effect the PDB (and therefore don't effect DAC). Specifically, we rebase - // our DLLs at the end of a build, that changes the PE file, but not the PDB. + // our DLLs at the end of a build, that changes the PE pPEAssembly, but not the PDB. // Note that if we wanted to be extra careful, we could read the CV contents (which includes // the GUID signature) and verify it matches. Using the timestamp is useful for helpful error // messages, and should be sufficient in any real scenario. diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index 20bb5ff3e0847..c6af447d1b13a 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -272,7 +272,7 @@ DacDbiInterfaceImpl::DacDbiInterfaceImpl( ) : ClrDataAccess(pTarget), m_pAllocator(pAllocator), m_pMetaDataLookup(pMetaDataLookup), - m_pCachedPEFile(VMPTR_PEFile::NullPtr()), + m_pCachedPEAssembly(VMPTR_PEAssembly::NullPtr()), m_pCachedImporter(NULL), m_isCachedHijackFunctionValid(FALSE) { @@ -307,7 +307,7 @@ DacDbiInterfaceImpl::~DacDbiInterfaceImpl() // Called from DAC-ized code to get a IMDInternalImport // // Arguments: -// pPEFile - PE file for which to get importer for +// pPEAssembly - PE file for which to get importer for // fThrowEx - if true, throw instead of returning NULL. // // Returns: @@ -324,7 +324,7 @@ DacDbiInterfaceImpl::~DacDbiInterfaceImpl() // This is an Internal importer, not a public Metadata importer. // interface IMDInternalImport* DacDbiInterfaceImpl::GetMDImport( - const PEFile* pPEFile, + const PEAssembly* pPEAssembly, const ReflectionModule * pReflectionModule, bool fThrowEx) { @@ -335,23 +335,23 @@ interface IMDInternalImport* DacDbiInterfaceImpl::GetMDImport( IDacDbiInterface::IMetaDataLookup * pLookup = m_pMetaDataLookup; _ASSERTE(pLookup != NULL); - VMPTR_PEFile vmPEFile = VMPTR_PEFile::NullPtr(); + VMPTR_PEAssembly vmPEAssembly = VMPTR_PEAssembly::NullPtr(); - if (pPEFile != NULL) + if (pPEAssembly != NULL) { - vmPEFile.SetHostPtr(pPEFile); + vmPEAssembly.SetHostPtr(pPEAssembly); } else if (pReflectionModule != NULL) { // SOS and ClrDataAccess rely on special logic to find the metadata for methods in dynamic modules. // We don't need to. The RS has already taken care of the special logic for us. - // So here we just grab the PEFile off of the ReflectionModule and continue down the normal + // So here we just grab the PEAssembly off of the ReflectionModule and continue down the normal // code path. See code:ClrDataAccess::GetMDImport for comparison. - vmPEFile.SetHostPtr(pReflectionModule->GetFile()); + vmPEAssembly.SetHostPtr(pReflectionModule->GetPEAssembly()); } // Optimize for the case where the VM queries the same Importer many times in a row. - if (m_pCachedPEFile == vmPEFile) + if (m_pCachedPEAssembly == vmPEAssembly) { return m_pCachedImporter; } @@ -370,7 +370,7 @@ interface IMDInternalImport* DacDbiInterfaceImpl::GetMDImport( // To get the old codepath that uses the v2 metadata lookup methods, // you'd have to load DAC only and then you'll get ClrDataAccess's implementation // of this function. - pInternal = pLookup->LookupMetaData(vmPEFile, isILMetaDataForNI); + pInternal = pLookup->LookupMetaData(vmPEAssembly, isILMetaDataForNI); } EX_CATCH { @@ -397,7 +397,7 @@ interface IMDInternalImport* DacDbiInterfaceImpl::GetMDImport( else { // Cache it such that it we look for the exact same Importer again, we'll return it. - m_pCachedPEFile = vmPEFile; + m_pCachedPEAssembly = vmPEAssembly; m_pCachedImporter = pInternal; } @@ -445,7 +445,7 @@ HRESULT DacDbiInterfaceImpl::FlushCache() // That would remove host DAC instances while they're being used. DD_NON_REENTRANT_MAY_THROW; - m_pCachedPEFile = VMPTR_PEFile::NullPtr(); + m_pCachedPEAssembly = VMPTR_PEAssembly::NullPtr(); m_pCachedImporter = NULL; m_isCachedHijackFunctionValid = FALSE; @@ -1227,7 +1227,7 @@ mdSignature DacDbiInterfaceImpl::GetILCodeAndSigHelper(Module * pModule, } -bool DacDbiInterfaceImpl::GetMetaDataFileInfoFromPEFile(VMPTR_PEFile vmPEFile, +bool DacDbiInterfaceImpl::GetMetaDataFileInfoFromPEFile(VMPTR_PEAssembly vmPEAssembly, DWORD &dwTimeStamp, DWORD &dwSize, bool &isNGEN, @@ -1237,14 +1237,14 @@ bool DacDbiInterfaceImpl::GetMetaDataFileInfoFromPEFile(VMPTR_PEFile vmPEFile, DWORD dwDataSize; DWORD dwRvaHint; - PEFile * pPEFile = vmPEFile.GetDacPtr(); - _ASSERTE(pPEFile != NULL); - if (pPEFile == NULL) + PEAssembly * pPEAssembly = vmPEAssembly.GetDacPtr(); + _ASSERTE(pPEAssembly != NULL); + if (pPEAssembly == NULL) return false; WCHAR wszFilePath[MAX_LONGPATH] = {0}; DWORD cchFilePath = MAX_LONGPATH; - bool ret = ClrDataAccess::GetMetaDataFileInfoFromPEFile(pPEFile, + bool ret = ClrDataAccess::GetMetaDataFileInfoFromPEFile(pPEAssembly, dwTimeStamp, dwSize, dwDataSize, @@ -1258,7 +1258,7 @@ bool DacDbiInterfaceImpl::GetMetaDataFileInfoFromPEFile(VMPTR_PEFile vmPEFile, } -bool DacDbiInterfaceImpl::GetILImageInfoFromNgenPEFile(VMPTR_PEFile vmPEFile, +bool DacDbiInterfaceImpl::GetILImageInfoFromNgenPEFile(VMPTR_PEAssembly vmPEAssembly, DWORD &dwTimeStamp, DWORD &dwSize, IStringHolder* pStrFilename) @@ -4137,16 +4137,16 @@ BOOL DacDbiInterfaceImpl::GetModulePath(VMPTR_Module vmModule, DD_ENTER_MAY_THROW; Module * pModule = vmModule.GetDacPtr(); - PEFile * pFile = pModule->GetFile(); - if (pFile != NULL) + PEAssembly * pPEAssembly = pModule->GetPEAssembly(); + if (pPEAssembly != NULL) { - if( !pFile->GetPath().IsEmpty() ) + if( !pPEAssembly->GetPath().IsEmpty() ) { // Module has an on-disk path - const WCHAR * szPath = pFile->GetPath().DacGetRawUnicode(); + const WCHAR * szPath = pPEAssembly->GetPath().DacGetRawUnicode(); if (szPath == NULL) { - szPath = pFile->GetModuleFileNameHint().DacGetRawUnicode(); + szPath = pPEAssembly->GetModuleFileNameHint().DacGetRawUnicode(); if (szPath == NULL) { goto NoFileName; @@ -4198,12 +4198,12 @@ HRESULT DacDbiInterfaceImpl::IsModuleMapped(VMPTR_Module pModule, OUT BOOL *isMo EX_TRY { - PTR_PEFile pPEFile = pTargetModule->GetFile(); - _ASSERTE(pPEFile != NULL); + PTR_PEAssembly pPEAssembly = pTargetModule->GetPEAssembly(); + _ASSERTE(pPEAssembly != NULL); - if (pPEFile->HasLoadedIL()) + if (pPEAssembly->HasLoadedPEImage()) { - *isModuleMapped = pPEFile->GetLoadedIL()->IsMapped(); + *isModuleMapped = pPEAssembly->GetLoadedLayout()->IsMapped(); hr = S_OK; } } @@ -4296,11 +4296,11 @@ void DacDbiInterfaceImpl::GetMetadata(VMPTR_Module vmModule, TargetBuffer * pTar } else { - PEFile * pFile = pModule->GetFile(); + PEAssembly * pPEAssembly = pModule->GetPEAssembly(); // For non-dynamic modules, metadata is in the pe-image. COUNT_T size; - CORDB_ADDRESS address = PTR_TO_CORDB_ADDRESS(dac_cast(pFile->GetLoadedMetadata(&size))); + CORDB_ADDRESS address = PTR_TO_CORDB_ADDRESS(dac_cast(pPEAssembly->GetLoadedMetadata(&size))); pTargetBuffer->Init(address, (ULONG) size); } @@ -4386,9 +4386,9 @@ void DacDbiInterfaceImpl::GetModuleData(VMPTR_Module vmModule, ModuleInfo * pDat ZeroMemory(pData, sizeof(*pData)); Module * pModule = vmModule.GetDacPtr(); - PEFile * pFile = pModule->GetFile(); + PEAssembly * pPEAssembly = pModule->GetPEAssembly(); - pData->vmPEFile.SetHostPtr(pFile); + pData->vmPEAssembly.SetHostPtr(pPEAssembly); pData->vmAssembly.SetHostPtr(pModule->GetAssembly()); // Is it dynamic? @@ -4403,15 +4403,15 @@ void DacDbiInterfaceImpl::GetModuleData(VMPTR_Module vmModule, ModuleInfo * pDat if (!fIsDynamic) { COUNT_T size = 0; - pData->pPEBaseAddress = PTR_TO_TADDR(pFile->GetDebuggerContents(&size)); + pData->pPEBaseAddress = PTR_TO_TADDR(pPEAssembly->GetDebuggerContents(&size)); pData->nPESize = (ULONG) size; } // In-memory is determined by whether the module has a filename. pData->fInMemory = FALSE; - if (pFile != NULL) + if (pPEAssembly != NULL) { - pData->fInMemory = pFile->GetPath().IsEmpty(); + pData->fInMemory = pPEAssembly->GetPath().IsEmpty(); } } @@ -7318,13 +7318,13 @@ void DacDbiInterfaceImpl::GetGCHeapInformation(COR_HEAPINFO * pHeapInfo) } -HRESULT DacDbiInterfaceImpl::GetPEFileMDInternalRW(VMPTR_PEFile vmPEFile, OUT TADDR* pAddrMDInternalRW) +HRESULT DacDbiInterfaceImpl::GetPEFileMDInternalRW(VMPTR_PEAssembly vmPEAssembly, OUT TADDR* pAddrMDInternalRW) { DD_ENTER_MAY_THROW; if (pAddrMDInternalRW == NULL) return E_INVALIDARG; - PEFile * pPEFile = vmPEFile.GetDacPtr(); - *pAddrMDInternalRW = pPEFile->GetMDInternalRWAddress(); + PEAssembly * pPEAssembly = vmPEAssembly.GetDacPtr(); + *pAddrMDInternalRW = pPEAssembly->GetMDInternalRWAddress(); return S_OK; } diff --git a/src/coreclr/debug/daccess/dacdbiimpl.h b/src/coreclr/debug/daccess/dacdbiimpl.h index 3088ed007a717..e484a28008b0c 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.h +++ b/src/coreclr/debug/daccess/dacdbiimpl.h @@ -53,7 +53,7 @@ class DacDbiInterfaceImpl : // Overridden from ClrDataAccess. Gets an internal metadata importer for the file. virtual IMDInternalImport* GetMDImport( - const PEFile* pPEFile, + const PEAssembly* pPEAssembly, const ReflectionModule * pReflectionModule, bool fThrowEx); @@ -149,7 +149,7 @@ class DacDbiInterfaceImpl : HRESULT GetTypeLayout(COR_TYPEID id, COR_TYPE_LAYOUT *pLayout); HRESULT GetArrayLayout(COR_TYPEID id, COR_ARRAY_LAYOUT *pLayout); void GetGCHeapInformation(COR_HEAPINFO * pHeapInfo); - HRESULT GetPEFileMDInternalRW(VMPTR_PEFile vmPEFile, OUT TADDR* pAddrMDInternalRW); + HRESULT GetPEFileMDInternalRW(VMPTR_PEAssembly vmPEAssembly, OUT TADDR* pAddrMDInternalRW); HRESULT GetReJitInfo(VMPTR_Module vmModule, mdMethodDef methodTk, OUT VMPTR_ReJitInfo* pReJitInfo); HRESULT GetActiveRejitILCodeVersionNode(VMPTR_Module vmModule, mdMethodDef methodTk, OUT VMPTR_ILCodeVersionNode* pVmILCodeVersionNode); HRESULT GetReJitInfo(VMPTR_MethodDesc vmMethod, CORDB_ADDRESS codeStartAddress, OUT VMPTR_ReJitInfo* pReJitInfo); @@ -952,15 +952,15 @@ class DacDbiInterfaceImpl : IMetaDataLookup * m_pMetaDataLookup; - // Metadata lookups is just a property on the PEFile in the normal builds, + // Metadata lookups is just a property on the PEAssembly in the normal builds, // and so VM code tends to access the same metadata importer many times in a row. // Cache the most-recently used to avoid excessive redundant lookups. - // PEFile of Cached Importer. Invalidated between Flush calls. If this is Non-null, + // PEAssembly of Cached Importer. Invalidated between Flush calls. If this is Non-null, // then the importer is m_pCachedImporter, and we can avoid using IMetaDataLookup - VMPTR_PEFile m_pCachedPEFile; + VMPTR_PEAssembly m_pCachedPEAssembly; - // Value of cached importer, corresponds with m_pCachedPEFile. + // Value of cached importer, corresponds with m_pCachedPEAssembly. IMDInternalImport * m_pCachedImporter; // Value of cached hijack function list, corresponds to g_pDebugger->m_rgHijackFunction @@ -1104,13 +1104,13 @@ class DacDbiInterfaceImpl : public: // APIs for picking up the info needed for a debugger to look up an ngen image or IL image // from it's search path. - bool GetMetaDataFileInfoFromPEFile(VMPTR_PEFile vmPEFile, + bool GetMetaDataFileInfoFromPEFile(VMPTR_PEAssembly vmPEAssembly, DWORD &dwTimeStamp, DWORD &dwSize, bool &isNGEN, IStringHolder* pStrFilename); - bool GetILImageInfoFromNgenPEFile(VMPTR_PEFile vmPEFile, + bool GetILImageInfoFromNgenPEFile(VMPTR_PEAssembly vmPEAssembly, DWORD &dwTimeStamp, DWORD &dwSize, IStringHolder* pStrFilename); diff --git a/src/coreclr/debug/daccess/dacfn.cpp b/src/coreclr/debug/daccess/dacfn.cpp index c8ac208d6352c..e1b83aba17e20 100644 --- a/src/coreclr/debug/daccess/dacfn.cpp +++ b/src/coreclr/debug/daccess/dacfn.cpp @@ -1304,7 +1304,7 @@ DacSetMethodDescEnumerated(LPCVOID pMD) // This gets called from DAC-ized code in the VM. IMDInternalImport* -DacGetMDImport(const PEFile* peFile, bool throwEx) +DacGetMDImport(const PEAssembly* pPEAssembly, bool throwEx) { if (!g_dacImpl) { @@ -1312,7 +1312,7 @@ DacGetMDImport(const PEFile* peFile, bool throwEx) UNREACHABLE(); } - return g_dacImpl->GetMDImport(peFile, throwEx); + return g_dacImpl->GetMDImport(pPEAssembly, throwEx); } IMDInternalImport* diff --git a/src/coreclr/debug/daccess/dacimpl.h b/src/coreclr/debug/daccess/dacimpl.h index 62d99ec574929..84d560e73ec03 100644 --- a/src/coreclr/debug/daccess/dacimpl.h +++ b/src/coreclr/debug/daccess/dacimpl.h @@ -132,7 +132,7 @@ class ReflectionModule; struct DAC_MD_IMPORT { DAC_MD_IMPORT* next; // list link field - TADDR peFile; // a TADDR for a PEFile* or a ReflectionModule* + TADDR peFile; // a TADDR for a PEAssembly* or a ReflectionModule* IMDInternalImport* impl; // Associated metadata interface bool isAlternate; // for NGEN images set to true if the metadata corresponds to the IL image @@ -151,7 +151,7 @@ struct DAC_MD_IMPORT // This class maintains a cache of IMDInternalImport* and their corresponding -// source (a PEFile* or a ReflectionModule*), as a singly-linked list of +// source (a PEAssembly* or a ReflectionModule*), as a singly-linked list of // DAC_MD_IMPORT nodes. The cache is flushed whenever the process state changes // by calling its Flush() member function. class MDImportsCache @@ -1352,18 +1352,18 @@ class ClrDataAccess JITNotification* GetHostJitNotificationTable(); GcNotification* GetHostGcNotificationTable(); - void* GetMetaDataFromHost(PEFile* peFile, + void* GetMetaDataFromHost(PEAssembly* pPEAssembly, bool* isAlternate); virtual - interface IMDInternalImport* GetMDImport(const PEFile* peFile, + interface IMDInternalImport* GetMDImport(const PEAssembly* pPEAssembly, const ReflectionModule* reflectionModule, bool throwEx); - interface IMDInternalImport* GetMDImport(const PEFile* peFile, + interface IMDInternalImport* GetMDImport(const PEAssembly* pPEAssembly, bool throwEx) { - return GetMDImport(peFile, NULL, throwEx); + return GetMDImport(pPEAssembly, NULL, throwEx); } interface IMDInternalImport* GetMDImport(const ReflectionModule* reflectionModule, @@ -1501,7 +1501,7 @@ class ClrDataAccess public: // APIs for picking up the info needed for a debugger to look up an ngen image or IL image // from it's search path. - static bool GetMetaDataFileInfoFromPEFile(PEFile *pPEFile, + static bool GetMetaDataFileInfoFromPEFile(PEAssembly *pPEAssembly, DWORD &dwImageTimestamp, DWORD &dwImageSize, DWORD &dwDataSize, @@ -1510,7 +1510,7 @@ class ClrDataAccess __out_ecount(cchFilePath) LPWSTR wszFilePath, DWORD cchFilePath); - static bool GetILImageInfoFromNgenPEFile(PEFile *peFile, + static bool GetILImageInfoFromNgenPEFile(PEAssembly *pPEAssembly, DWORD &dwTimeStamp, DWORD &dwSize, __out_ecount(cchPath) LPWSTR wszPath, diff --git a/src/coreclr/debug/daccess/enummem.cpp b/src/coreclr/debug/daccess/enummem.cpp index f956151f7ce8e..1914d13a1c5fb 100644 --- a/src/coreclr/debug/daccess/enummem.cpp +++ b/src/coreclr/debug/daccess/enummem.cpp @@ -67,7 +67,7 @@ HRESULT ClrDataAccess::EnumMemCollectImages() ProcessModIter modIter; Module* modDef = NULL; HRESULT status = S_OK; - PEFile *file; + PEAssembly *assembly; TADDR pStartAddr = 0; ULONG32 ulSize = 0; ULONG32 ulSizeBlock; @@ -84,7 +84,7 @@ HRESULT ClrDataAccess::EnumMemCollectImages() EX_TRY { ulSize = 0; - file = modDef->GetFile(); + assembly = modDef->GetPEAssembly(); // We want to save any in-memory images. These show up like mapped files // and so would not be in a heap dump by default. Technically it's not clear we @@ -92,13 +92,11 @@ HRESULT ClrDataAccess::EnumMemCollectImages() // after-the-fact. But in-memory modules may be harder to track down at debug time // and people may have come to rely on this - so we'll include them for now. if ( - file->GetPath().IsEmpty() && // is in-memory - file->HasMetadata() && // skip resource assemblies - file->IsLoaded(FALSE) && // skip files not yet loaded - !file->IsDynamic()) // skip dynamic (GetLoadedIL asserts anyway) + assembly->GetPath().IsEmpty() && // is in-memory + assembly->HasLoadedPEImage()) // skip files not yet loaded or Dynamic { - pStartAddr = PTR_TO_TADDR(file->GetLoadedIL()->GetBase()); - ulSize = file->GetLoadedIL()->GetSize(); + pStartAddr = PTR_TO_TADDR(assembly->GetLoadedLayout()->GetBase()); + ulSize = assembly->GetLoadedLayout()->GetSize(); } // memory are mapped in in GetOsPageSize() size. @@ -609,7 +607,7 @@ HRESULT ClrDataAccess::EnumMemDumpModuleList(CLRDataEnumMemoryFlags flags) Module* modDef; TADDR base; ULONG32 length; - PEFile *file; + PEAssembly* assembly; TSIZE_T cbMemoryReported = m_cbMemoryReported; // @@ -635,8 +633,8 @@ HRESULT ClrDataAccess::EnumMemDumpModuleList(CLRDataEnumMemoryFlags flags) // To enable a debugger to check on whether a module is an NI or IL image, they need // the DOS header, PE headers, and IMAGE_COR20_HEADER for the Flags member. // We expose no API today to find this out. - PTR_PEFile pPEFile = modDef->GetFile(); - PEImage * pILImage = pPEFile->GetILimage(); + PTR_PEAssembly pPEAssembly = modDef->GetPEAssembly(); + PEImage * pILImage = pPEAssembly->GetPEImage(); // Implicitly gets the COR header. if ((pILImage) && (pILImage->HasLoadedLayout())) @@ -649,9 +647,9 @@ HRESULT ClrDataAccess::EnumMemDumpModuleList(CLRDataEnumMemoryFlags flags) EX_TRY { - file = modDef->GetFile(); - base = PTR_TO_TADDR(file->GetLoadedImageContents(&length)); - file->EnumMemoryRegions(flags); + assembly = modDef->GetPEAssembly(); + base = PTR_TO_TADDR(assembly->GetLoadedImageContents(&length)); + assembly->EnumMemoryRegions(flags); } EX_CATCH { diff --git a/src/coreclr/debug/daccess/request.cpp b/src/coreclr/debug/daccess/request.cpp index 0048e43c3d018..8d6ae0b3bb85f 100644 --- a/src/coreclr/debug/daccess/request.cpp +++ b/src/coreclr/debug/daccess/request.cpp @@ -1506,8 +1506,8 @@ ClrDataAccess::GetObjectClassName(CLRDATA_ADDRESS obj, unsigned int count, __out { // There is a case where metadata was unloaded and the AppendType call will fail. // This is when an AppDomain has been unloaded but not yet collected. - PEFile *pPEFile = mt->GetModule()->GetFile(); - if (pPEFile->GetILimage() == NULL) + PEAssembly *pPEAssembly = mt->GetModule()->GetPEAssembly(); + if (pPEAssembly->GetPEImage() == NULL) { if (pNeeded) *pNeeded = 16; @@ -1645,14 +1645,14 @@ ClrDataAccess::GetModuleData(CLRDATA_ADDRESS addr, struct DacpModuleData *Module ZeroMemory(ModuleData,sizeof(DacpModuleData)); ModuleData->Address = addr; - ModuleData->File = HOST_CDADDR(pModule->GetFile()); + ModuleData->PEAssembly = HOST_CDADDR(pModule->GetPEAssembly()); COUNT_T metadataSize = 0; - if (!pModule->GetFile()->IsDynamic()) + if (!pModule->GetPEAssembly()->IsDynamic()) { - ModuleData->ilBase = (CLRDATA_ADDRESS)(ULONG_PTR) pModule->GetFile()->GetIJWBase(); + ModuleData->ilBase = (CLRDATA_ADDRESS)(ULONG_PTR) pModule->GetPEAssembly()->GetIJWBase(); } - ModuleData->metadataStart = (CLRDATA_ADDRESS)dac_cast(pModule->GetFile()->GetLoadedMetadata(&metadataSize)); + ModuleData->metadataStart = (CLRDATA_ADDRESS)dac_cast(pModule->GetPEAssembly()->GetLoadedMetadata(&metadataSize)); ModuleData->metadataSize = (SIZE_T) metadataSize; ModuleData->bIsReflection = pModule->IsReflection(); @@ -1770,8 +1770,8 @@ ClrDataAccess::GetMethodTableName(CLRDATA_ADDRESS mt, unsigned int count, __out_ { // There is a case where metadata was unloaded and the AppendType call will fail. // This is when an AppDomain has been unloaded but not yet collected. - PEFile *pPEFile = pMT->GetModule()->GetFile(); - if (pPEFile->GetILimage() == NULL) + PEAssembly *pPEAssembly = pMT->GetModule()->GetPEAssembly(); + if (pPEAssembly->GetPEImage() == NULL) { if (pNeeded) *pNeeded = 16; @@ -2053,19 +2053,18 @@ ClrDataAccess::GetPEFileName(CLRDATA_ADDRESS addr, unsigned int count, __out_z _ return E_INVALIDARG; SOSDacEnter(); - PEFile* pPEFile = PTR_PEFile(TO_TADDR(addr)); + PEAssembly* pPEAssembly = PTR_PEAssembly(TO_TADDR(addr)); // Turn from bytes to wide characters - if (!pPEFile->GetPath().IsEmpty()) + if (!pPEAssembly->GetPath().IsEmpty()) { - if (!pPEFile->GetPath().DacGetUnicode(count, fileName, pNeeded)) + if (!pPEAssembly->GetPath().DacGetUnicode(count, fileName, pNeeded)) hr = E_FAIL; } - else if (!pPEFile->IsDynamic()) + else if (!pPEAssembly->IsDynamic()) { - PEAssembly *pAssembly = pPEFile->GetAssembly(); StackSString displayName; - pAssembly->GetDisplayName(displayName, 0); + pPEAssembly->GetDisplayName(displayName, 0); if (displayName.IsEmpty()) { @@ -2112,11 +2111,11 @@ ClrDataAccess::GetPEFileBase(CLRDATA_ADDRESS addr, CLRDATA_ADDRESS *base) SOSDacEnter(); - PEFile* pPEFile = PTR_PEFile(TO_TADDR(addr)); + PEAssembly* pPEAssembly = PTR_PEAssembly(TO_TADDR(addr)); // More fields later? - if (!pPEFile->IsDynamic()) - *base = TO_CDADDR(pPEFile->GetIJWBase()); + if (!pPEAssembly->IsDynamic()) + *base = TO_CDADDR(pPEAssembly->GetIJWBase()); else *base = NULL; @@ -4768,8 +4767,8 @@ HRESULT ClrDataAccess::GetAssemblyLoadContext(CLRDATA_ADDRESS methodTable, CLRDA PTR_MethodTable pMT = PTR_MethodTable(CLRDATA_ADDRESS_TO_TADDR(methodTable)); PTR_Module pModule = pMT->GetModule(); - PTR_PEFile pPEFile = pModule->GetFile(); - PTR_AssemblyBinder pBinder = pPEFile->GetAssemblyBinder(); + PTR_PEAssembly pPEAssembly = pModule->GetPEAssembly(); + PTR_AssemblyBinder pBinder = pPEAssembly->GetAssemblyBinder(); INT_PTR managedAssemblyLoadContextHandle = pBinder->GetManagedAssemblyLoadContext(); diff --git a/src/coreclr/debug/daccess/task.cpp b/src/coreclr/debug/daccess/task.cpp index 8989977880e2e..5c8c3d0152dcf 100644 --- a/src/coreclr/debug/daccess/task.cpp +++ b/src/coreclr/debug/daccess/task.cpp @@ -2414,12 +2414,12 @@ ClrDataModule::GetFileName( { COUNT_T _nameLen; - // Try to get the file name through GetPath. - // If the returned name is empty, then try to get the guessed module file name. - // The guessed file name is propogated from metadata's module name. + // Try to get the assembly name through GetPath. + // If the returned name is empty, then try to get the guessed module assembly name. + // The guessed assembly name is propogated from metadata's module name. // - if ((m_module->GetFile()->GetPath().DacGetUnicode(bufLen, name, &_nameLen) && name[0])|| - (m_module->GetFile()->GetModuleFileNameHint().DacGetUnicode(bufLen, name, &_nameLen) && name[0])) + if ((m_module->GetPEAssembly()->GetPath().DacGetUnicode(bufLen, name, &_nameLen) && name[0])|| + (m_module->GetPEAssembly()->GetModuleFileNameHint().DacGetUnicode(bufLen, name, &_nameLen) && name[0])) { if (nameLen) { @@ -2455,19 +2455,11 @@ ClrDataModule::GetVersionId( EX_TRY { - if (!m_module->GetFile()->HasMetadata()) + GUID mdVid; + status = m_module->GetMDImport()->GetScopeProps(NULL, &mdVid); + if (SUCCEEDED(status)) { - status = E_NOINTERFACE; - } - else - { - GUID mdVid; - - status = m_module->GetMDImport()->GetScopeProps(NULL, &mdVid); - if (SUCCEEDED(status)) - { - *vid = mdVid; - } + *vid = mdVid; } } EX_CATCH @@ -2499,10 +2491,7 @@ ClrDataModule::GetFlags( { (*flags) |= CLRDATA_MODULE_IS_DYNAMIC; } - if (m_module->IsIStream()) - { - (*flags) |= CLRDATA_MODULE_IS_MEMORY_STREAM; - } + PTR_Assembly pAssembly = m_module->GetAssembly(); PTR_BaseDomain pBaseDomain = pAssembly->GetDomain(); if (pBaseDomain->IsAppDomain()) @@ -2568,8 +2557,8 @@ ClrDataModule::StartEnumExtents( { if (!m_setExtents) { - PEFile* file = m_module->GetFile(); - if (!file) + PEAssembly* assembly = m_module->GetPEAssembly(); + if (!assembly) { *handle = 0; status = E_INVALIDARG; @@ -2578,10 +2567,10 @@ ClrDataModule::StartEnumExtents( CLRDATA_MODULE_EXTENT* extent = m_extents; - if (file->GetLoadedImageContents() != NULL) + if (assembly->GetLoadedImageContents() != NULL) { extent->base = - TO_CDADDR( PTR_TO_TADDR(file->GetLoadedImageContents(&extent->length)) ); + TO_CDADDR( PTR_TO_TADDR(assembly->GetLoadedImageContents(&extent->length)) ); extent->type = CLRDATA_MODULE_PE_FILE; extent++; } @@ -2723,23 +2712,23 @@ ClrDataModule::RequestGetModuleData( ZeroMemory(outGMD, sizeof(DacpGetModuleData)); Module* pModule = GetModule(); - PEFile *pPEFile = pModule->GetFile(); + PEAssembly *pPEAssembly = pModule->GetPEAssembly(); - outGMD->PEFile = TO_CDADDR(PTR_HOST_TO_TADDR(pPEFile)); + outGMD->PEAssembly = TO_CDADDR(PTR_HOST_TO_TADDR(pPEAssembly)); outGMD->IsDynamic = pModule->IsReflection(); - if (pPEFile != NULL) + if (pPEAssembly != NULL) { - outGMD->IsInMemory = pPEFile->GetPath().IsEmpty(); + outGMD->IsInMemory = pPEAssembly->GetPath().IsEmpty(); COUNT_T peSize; - outGMD->LoadedPEAddress = TO_CDADDR(PTR_TO_TADDR(pPEFile->GetLoadedImageContents(&peSize))); + outGMD->LoadedPEAddress = TO_CDADDR(PTR_TO_TADDR(pPEAssembly->GetLoadedImageContents(&peSize))); outGMD->LoadedPESize = (ULONG64)peSize; - // Can not get the file layout for a dynamic module + // Can not get the assembly layout for a dynamic module if (!outGMD->IsDynamic) { - outGMD->IsFileLayout = pPEFile->GetLoaded()->IsFlat(); + outGMD->IsFileLayout = pPEAssembly->GetLoadedLayout()->IsFlat(); } } @@ -2876,17 +2865,10 @@ ClrDataModule::GetMdInterface(PVOID* retIface) { if (m_mdImport == NULL) { - if (!m_module->GetFile()->HasMetadata()) - { - status = E_NOINTERFACE; - goto Exit; - } - // // Make sure internal MD is in RW format. // IMDInternalImport* rwMd; - status = ConvertMDInternalImport(m_module->GetMDImport(), &rwMd); if (FAILED(status)) { diff --git a/src/coreclr/debug/di/module.cpp b/src/coreclr/debug/di/module.cpp index e662f8cb86e24..b48e12afc96be 100644 --- a/src/coreclr/debug/di/module.cpp +++ b/src/coreclr/debug/di/module.cpp @@ -82,7 +82,7 @@ CordbModule::CordbModule( m_fDynamic = modInfo.fIsDynamic; m_fInMemory = modInfo.fInMemory; - m_vmPEFile = modInfo.vmPEFile; + m_vmPEFile = modInfo.vmPEAssembly; if (!vmDomainFile.IsNull()) { @@ -268,13 +268,13 @@ IDacDbiInterface::SymbolFormat CordbModule::GetInMemorySymbolStream(IStream ** p // Accessor for PE file. // // Returns: -// VMPTR_PEFile for this module. Should always be non-null +// VMPTR_PEAssembly for this module. Should always be non-null // // Notes: // A main usage of this is to find the proper internal MetaData importer. -// DACized code needs to map from PEFile --> IMDInternalImport. +// DACized code needs to map from PEAssembly --> IMDInternalImport. // -VMPTR_PEFile CordbModule::GetPEFile() +VMPTR_PEAssembly CordbModule::GetPEFile() { return m_vmPEFile; } diff --git a/src/coreclr/debug/di/process.cpp b/src/coreclr/debug/di/process.cpp index 134e3dbab4c96..c6c16d0700a85 100644 --- a/src/coreclr/debug/di/process.cpp +++ b/src/coreclr/debug/di/process.cpp @@ -290,10 +290,10 @@ static inline DWORD CordbGetWaitTimeout() //---------------------------------------------------------------------------- // Implementation of IDacDbiInterface::IMetaDataLookup. -// lookup Internal Metadata Importer keyed by PEFile +// lookup Internal Metadata Importer keyed by PEAssembly // isILMetaDataForNGENImage is true iff the IMDInternalImport returned represents a pointer to // metadata from an IL image when the module was an ngen'ed image. -IMDInternalImport * CordbProcess::LookupMetaData(VMPTR_PEFile vmPEFile, bool &isILMetaDataForNGENImage) +IMDInternalImport * CordbProcess::LookupMetaData(VMPTR_PEAssembly vmPEAssembly, bool &isILMetaDataForNGENImage) { INTERNAL_DAC_CALLBACK(this); @@ -312,7 +312,7 @@ IMDInternalImport * CordbProcess::LookupMetaData(VMPTR_PEFile vmPEFile, bool &is pModule != NULL; pModule = pAppDomain->m_modules.FindNext(&hashFindModule)) { - if (pModule->GetPEFile() == vmPEFile) + if (pModule->GetPEFile() == vmPEAssembly) { pMDII = NULL; ALLOW_DATATARGET_MISSING_MEMORY( @@ -341,7 +341,7 @@ IMDInternalImport * CordbProcess::LookupMetaData(VMPTR_PEFile vmPEFile, bool &is pModule != NULL; pModule = pAppDomain->m_modules.FindNext(&hashFindModule)) { - if (pModule->GetPEFile() == vmPEFile) + if (pModule->GetPEFile() == vmPEAssembly) { pMDII = NULL; ALLOW_DATATARGET_MISSING_MEMORY( @@ -354,7 +354,7 @@ IMDInternalImport * CordbProcess::LookupMetaData(VMPTR_PEFile vmPEFile, bool &is // debugger if it can find the metadata elsewhere. // If this was live debugging, we should have just gotten the memory contents. // Thus this code is for dump debugging, when you don't have the metadata in the dump. - pMDII = LookupMetaDataFromDebugger(vmPEFile, isILMetaDataForNGENImage, pModule); + pMDII = LookupMetaDataFromDebugger(vmPEAssembly, isILMetaDataForNGENImage, pModule); } return pMDII; } @@ -366,7 +366,7 @@ IMDInternalImport * CordbProcess::LookupMetaData(VMPTR_PEFile vmPEFile, bool &is IMDInternalImport * CordbProcess::LookupMetaDataFromDebugger( - VMPTR_PEFile vmPEFile, + VMPTR_PEAssembly vmPEAssembly, bool &isILMetaDataForNGENImage, CordbModule * pModule) { @@ -377,7 +377,7 @@ IMDInternalImport * CordbProcess::LookupMetaDataFromDebugger( IMDInternalImport * pMDII = NULL; // First, see if the debugger can locate the exact metadata we want. - if (this->GetDAC()->GetMetaDataFileInfoFromPEFile(vmPEFile, dwImageTimeStamp, dwImageSize, isNGEN, &filePath)) + if (this->GetDAC()->GetMetaDataFileInfoFromPEFile(vmPEAssembly, dwImageTimeStamp, dwImageSize, isNGEN, &filePath)) { _ASSERTE(filePath.IsSet()); @@ -408,7 +408,7 @@ IMDInternalImport * CordbProcess::LookupMetaDataFromDebugger( filePath.Clear(); if ((pMDII == NULL) && (isNGEN) && - (this->GetDAC()->GetILImageInfoFromNgenPEFile(vmPEFile, dwImageTimeStamp, dwImageSize, &filePath))) + (this->GetDAC()->GetILImageInfoFromNgenPEFile(vmPEAssembly, dwImageTimeStamp, dwImageSize, &filePath))) { _ASSERTE(filePath.IsSet()); @@ -496,7 +496,7 @@ IMDInternalImport * CordbProcess::LookupMetaDataFromDebuggerForSingleFile( if (SUCCEEDED(hr)) { // While we're successfully returning a metadata reader, remember that there's - // absolutely no guarantee this metadata is an exact match for the vmPEFile. + // absolutely no guarantee this metadata is an exact match for the vmPEAssembly. // The debugger could literally send us back a path to any managed file with // metadata content that is readable and we'll 'succeed'. // For now, this is by-design. A debugger should be allowed to decide if it wants diff --git a/src/coreclr/debug/di/rspriv.h b/src/coreclr/debug/di/rspriv.h index 7d257f6556e52..1baa6a9f24afd 100644 --- a/src/coreclr/debug/di/rspriv.h +++ b/src/coreclr/debug/di/rspriv.h @@ -2975,10 +2975,10 @@ class CordbProcess : //----------------------------------------------------------- // IMetaDataLookup // ----------------------------------------------------------- - IMDInternalImport * LookupMetaData(VMPTR_PEFile vmPEFile, bool &isILMetaDataForNGENImage); + IMDInternalImport * LookupMetaData(VMPTR_PEAssembly vmPEAssembly, bool &isILMetaDataForNGENImage); // Helper functions for LookupMetaData implementation - IMDInternalImport * LookupMetaDataFromDebugger(VMPTR_PEFile vmPEFile, + IMDInternalImport * LookupMetaDataFromDebugger(VMPTR_PEAssembly vmPEAssembly, bool &isILMetaDataForNGENImage, CordbModule * pModule); @@ -4368,7 +4368,7 @@ class CordbModule : public CordbBase, IDacDbiInterface::SymbolFormat GetInMemorySymbolStream(IStream ** ppStream); // accessor for PE file - VMPTR_PEFile GetPEFile(); + VMPTR_PEAssembly GetPEFile(); IMetaDataImport * GetMetaDataImporter(); @@ -4419,9 +4419,9 @@ class CordbModule : public CordbBase, // "Global" class for this module. Global functions + vars exist in this class. RSSmartPtr m_pClass; - // Handle to PEFile, useful for metadata lookups. + // Handle to PEAssembly, useful for metadata lookups. // this should always be non-null. - VMPTR_PEFile m_vmPEFile; + VMPTR_PEAssembly m_vmPEFile; // Public metadata importer. This is lazily initialized and accessed from code:GetMetaDataImporter diff --git a/src/coreclr/debug/ee/debugger.cpp b/src/coreclr/debug/ee/debugger.cpp index 2a8f565d686eb..0f61f8d36c78e 100644 --- a/src/coreclr/debug/ee/debugger.cpp +++ b/src/coreclr/debug/ee/debugger.cpp @@ -9537,21 +9537,6 @@ void Debugger::LoadModule(Module* pRuntimeModule, SENDIPCEVENT_END; - // need to update pdb stream for SQL passed in pdb stream - // regardless attach or not. - // - if (pRuntimeModule->IsIStream()) - { - // Just ignore failures. Caller was just sending a debug event and we don't - // want that to interop non-debugging functionality. - HRESULT hr = S_OK; - EX_TRY - { - SendUpdateModuleSymsEventAndBlock(pRuntimeModule, pAppDomain); - } - EX_CATCH_HRESULT(hr); - } - // Now that we're done with the load module event, can no longer change Jit flags. module->SetCanChangeJitFlags(false); } @@ -9710,7 +9695,7 @@ void Debugger::UnloadModule(Module* pRuntimeModule, STRESS_LOG6(LF_CORDB, LL_INFO10000, "D::UM: Unloading RTMod:%#08x (DomFile: %#08x, IsISStream:%#08x); DMod:%#08x(RTMod:%#08x DomFile: %#08x)\n", - pRuntimeModule, pRuntimeModule->GetDomainFile(), pRuntimeModule->IsIStream(), + pRuntimeModule, pRuntimeModule->GetDomainFile(), false, module, module->GetRuntimeModule(), module->GetDomainFile()); // Note: the appdomain the module was loaded in must match the appdomain we're unloading it from. If it doesn't, diff --git a/src/coreclr/debug/inc/dacdbiinterface.h b/src/coreclr/debug/inc/dacdbiinterface.h index 6696da0cbbbb0..0bda865bd0f59 100644 --- a/src/coreclr/debug/inc/dacdbiinterface.h +++ b/src/coreclr/debug/inc/dacdbiinterface.h @@ -521,7 +521,7 @@ class IDacDbiInterface // // For dynamic modules, the CLR will eagerly serialize the metadata at "debuggable" points. This // could be after each type is loaded; or after a bulk update. - // For non-dynamic modules (both in-memory and file-based), the metadata exists in the PEFile's image. + // For non-dynamic modules (both in-memory and file-based), the metadata exists in the PEAssembly's image. // // Failure cases: // This should succeed in normal, live-debugging scenarios. However, common failure paths here would be: @@ -2379,14 +2379,14 @@ class IDacDbiInterface CLR_DEBUGGING_PROCESS_FLAGS GetAttachStateFlags() = 0; virtual - bool GetMetaDataFileInfoFromPEFile(VMPTR_PEFile vmPEFile, + bool GetMetaDataFileInfoFromPEFile(VMPTR_PEAssembly vmPEAssembly, DWORD & dwTimeStamp, DWORD & dwImageSize, bool & isNGEN, IStringHolder* pStrFilename) = 0; virtual - bool GetILImageInfoFromNgenPEFile(VMPTR_PEFile vmPEFile, + bool GetILImageInfoFromNgenPEFile(VMPTR_PEAssembly vmPEAssembly, DWORD & dwTimeStamp, DWORD & dwSize, IStringHolder* pStrFilename) = 0; @@ -2512,17 +2512,17 @@ class IDacDbiInterface virtual void GetGCHeapInformation(OUT COR_HEAPINFO * pHeapInfo) = 0; - // If a PEFile has an RW capable IMDInternalImport, this returns the address of the MDInternalRW + // If a PEAssembly has an RW capable IMDInternalImport, this returns the address of the MDInternalRW // object which implements it. // // // Arguments: - // vmPEFile - target PEFile to get metadata MDInternalRW for. - // pAddrMDInternalRW - If a PEFile has an RW capable IMDInternalImport, this will be set to the address + // vmPEAssembly - target PEAssembly to get metadata MDInternalRW for. + // pAddrMDInternalRW - If a PEAssembly has an RW capable IMDInternalImport, this will be set to the address // of the MDInternalRW object which implements it. Otherwise it will be NULL. // virtual - HRESULT GetPEFileMDInternalRW(VMPTR_PEFile vmPEFile, OUT TADDR* pAddrMDInternalRW) = 0; + HRESULT GetPEFileMDInternalRW(VMPTR_PEAssembly vmPEAssembly, OUT TADDR* pAddrMDInternalRW) = 0; // DEPRECATED - use GetActiveRejitILCodeVersionNode // Retrieves the active ReJitInfo for a given module/methodDef, if it exists. @@ -2814,7 +2814,7 @@ class IDacDbiInterface { public: // - // Lookup a metadata importer via PEFile. + // Lookup a metadata importer via PEAssembly. // // Returns: // A IMDInternalImport used by dac-ized VM code. The object is NOT addref-ed. See lifespan notes below. @@ -2822,7 +2822,7 @@ class IDacDbiInterface // Throws on exceptional circumstances (eg, detects the debuggee is corrupted). // // Notes: - // IMDInternalImport is a property of PEFile. The DAC-ized code uses it as a weak reference, + // IMDInternalImport is a property of PEAssembly. The DAC-ized code uses it as a weak reference, // and so we avoid doing an AddRef() here because that would mean we need to add Release() calls // in DAC-only paths. // The metadata importers are not DAC-ized, and thus we have a local copy in the host. @@ -2837,7 +2837,7 @@ class IDacDbiInterface // - the reference count of the returned object is not adjusted. // virtual - IMDInternalImport * LookupMetaData(VMPTR_PEFile addressPEFile, bool &isILMetaDataForNGENImage) = 0; + IMDInternalImport * LookupMetaData(VMPTR_PEAssembly addressPEAssembly, bool &isILMetaDataForNGENImage) = 0; }; }; // end IDacDbiInterface diff --git a/src/coreclr/debug/inc/dacdbistructures.h b/src/coreclr/debug/inc/dacdbistructures.h index 19c788edb297f..5e02a68a792c9 100644 --- a/src/coreclr/debug/inc/dacdbistructures.h +++ b/src/coreclr/debug/inc/dacdbistructures.h @@ -186,10 +186,10 @@ struct MSLAYOUT ModuleInfo // (such as for a dynamic module that's not persisted to disk). CORDB_ADDRESS pPEBaseAddress; - // The PEFile associated with the module. Every module (even non-file-based ones) has a PEFile. + // The PEAssembly associated with the module. Every module (even non-file-based ones) has a PEAssembly. // This is critical because DAC may ask for a metadata importer via PE-file. - // a PEFile may have 1 or more PEImage child objects (1 for IL, 1 for native image, etc) - VMPTR_PEFile vmPEFile; + // a PEAssembly may have 1 or more PEImage child objects (1 for IL, 1 for native image, etc) + VMPTR_PEAssembly vmPEAssembly; // The PE Base address and size of the module. These may be 0 if there is no image // (such as for a dynamic module that's not persisted to disk). diff --git a/src/coreclr/debug/inc/dbgipcevents.h b/src/coreclr/debug/inc/dbgipcevents.h index da097631b2f8c..db4a00fca1c86 100644 --- a/src/coreclr/debug/inc/dbgipcevents.h +++ b/src/coreclr/debug/inc/dbgipcevents.h @@ -858,7 +858,7 @@ DEFINE_VMPTR(class Module, PTR_Module, VMPTR_Module); DEFINE_VMPTR(class DomainAssembly, PTR_DomainAssembly, VMPTR_DomainAssembly); DEFINE_VMPTR(class Assembly, PTR_Assembly, VMPTR_Assembly); -DEFINE_VMPTR(class PEFile, PTR_PEFile, VMPTR_PEFile); +DEFINE_VMPTR(class PEAssembly, PTR_PEAssembly, VMPTR_PEAssembly); DEFINE_VMPTR(class MethodDesc, PTR_MethodDesc, VMPTR_MethodDesc); DEFINE_VMPTR(class FieldDesc, PTR_FieldDesc, VMPTR_FieldDesc); diff --git a/src/coreclr/inc/corhdr.h b/src/coreclr/inc/corhdr.h index 58cb0aa7f9ca8..f9d4b5abf3b8b 100644 --- a/src/coreclr/inc/corhdr.h +++ b/src/coreclr/inc/corhdr.h @@ -227,7 +227,7 @@ typedef struct IMAGE_COR20_HEADER }; // This is the blob of managed resources. Fetched using code:AssemblyNative.GetResource and - // code:PEFile.GetResource and accessible from managed code from + // code:PEAssembly.GetResource and accessible from managed code from // System.Assembly.GetManifestResourceStream. The meta data has a table that maps names to offsets into // this blob, so logically the blob is a set of resources. IMAGE_DATA_DIRECTORY Resources; diff --git a/src/coreclr/inc/daccess.h b/src/coreclr/inc/daccess.h index 82009b77890e2..b769fcec22b4b 100644 --- a/src/coreclr/inc/daccess.h +++ b/src/coreclr/inc/daccess.h @@ -769,7 +769,7 @@ HRESULT DacReplacePatchesInHostMemory(MemoryRange range, PVOID pBuffer); #ifdef __cplusplus } class ReflectionModule; -interface IMDInternalImport* DacGetMDImport(const class PEFile* peFile, +interface IMDInternalImport* DacGetMDImport(const class PEAssembly* pPEAssembly, bool throwEx); interface IMDInternalImport* DacGetMDImport(const ReflectionModule* reflectionModule, bool throwEx); diff --git a/src/coreclr/inc/dacprivate.h b/src/coreclr/inc/dacprivate.h index 99419667a9635..45a4eda3cc140 100644 --- a/src/coreclr/inc/dacprivate.h +++ b/src/coreclr/inc/dacprivate.h @@ -230,8 +230,8 @@ struct MSLAYOUT DacpThreadLocalModuleData struct MSLAYOUT DacpModuleData { CLRDATA_ADDRESS Address = 0; - CLRDATA_ADDRESS File = 0; // A PEFile addr - CLRDATA_ADDRESS ilBase = 0; + CLRDATA_ADDRESS PEAssembly = 0; // A PEAssembly addr + CLRDATA_ADDRESS ilBase = 0; CLRDATA_ADDRESS metadataStart = 0; ULONG64 metadataSize = 0; CLRDATA_ADDRESS Assembly = 0; // Assembly pointer @@ -981,7 +981,7 @@ struct MSLAYOUT DacpGetModuleData BOOL IsDynamic = FALSE; BOOL IsInMemory = FALSE; BOOL IsFileLayout = FALSE; - CLRDATA_ADDRESS PEFile = 0; + CLRDATA_ADDRESS PEAssembly = 0; CLRDATA_ADDRESS LoadedPEAddress = 0; ULONG64 LoadedPESize = 0; CLRDATA_ADDRESS InMemoryPdbAddress = 0; diff --git a/src/coreclr/inc/vptr_list.h b/src/coreclr/inc/vptr_list.h index 4ea50d9fe4f44..4f8baccd5cd68 100644 --- a/src/coreclr/inc/vptr_list.h +++ b/src/coreclr/inc/vptr_list.h @@ -39,7 +39,6 @@ VPTR_CLASS(DelegateInvokeStubManager) VPTR_CLASS(TailCallStubManager) #endif VPTR_CLASS(CallCountingStubManager) -VPTR_CLASS(PEFile) VPTR_CLASS(PEAssembly) VPTR_CLASS(PEImageLayout) VPTR_CLASS(RawImageLayout) diff --git a/src/coreclr/vm/CMakeLists.txt b/src/coreclr/vm/CMakeLists.txt index da3a36468c06c..a1b164a0af65d 100644 --- a/src/coreclr/vm/CMakeLists.txt +++ b/src/coreclr/vm/CMakeLists.txt @@ -106,7 +106,7 @@ set(VM_SOURCES_DAC_AND_WKS_COMMON nativeimage.cpp object.cpp onstackreplacement.cpp - pefile.cpp + peassembly.cpp peimage.cpp perfmap.cpp perfinfo.cpp @@ -207,8 +207,8 @@ set(VM_HEADERS_DAC_AND_WKS_COMMON object.h object.inl onstackreplacement.h - pefile.h - pefile.inl + peassembly.h + peassembly.inl peimage.h peimage.inl peimagelayout.h diff --git a/src/coreclr/vm/appdomain.cpp b/src/coreclr/vm/appdomain.cpp index d76db29301529..61c4c08e24925 100644 --- a/src/coreclr/vm/appdomain.cpp +++ b/src/coreclr/vm/appdomain.cpp @@ -1041,7 +1041,6 @@ void SystemDomain::Attach() // Create the one and only app domain AppDomain::Create(); - AppDomain::GetCurrentDomain()->CreateBinderContext(); // Each domain gets its own ReJitManager, and ReJitManager has its own static // initialization to run @@ -1174,7 +1173,7 @@ void SystemDomain::Init() if (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_ZapDisable) != 0) g_fAllowNativeImages = false; - m_pSystemFile = NULL; + m_pSystemPEAssembly = NULL; m_pSystemAssembly = NULL; DWORD size = 0; @@ -1320,12 +1319,11 @@ void SystemDomain::LoadBaseSystemClasses() ETWOnStartup(LdSysBases_V1, LdSysBasesEnd_V1); - { - m_pSystemFile = PEAssembly::OpenSystem(); - } + m_pSystemPEAssembly = PEAssembly::OpenSystem(); + // Only partially load the system assembly. Other parts of the code will want to access // the globals in this function before finishing the load. - m_pSystemAssembly = DefaultDomain()->LoadDomainAssembly(NULL, m_pSystemFile, FILE_LOAD_POST_LOADLIBRARY)->GetCurrentAssembly(); + m_pSystemAssembly = DefaultDomain()->LoadDomainAssembly(NULL, m_pSystemPEAssembly, FILE_LOAD_POST_LOADLIBRARY)->GetCurrentAssembly(); // Set up binder for CoreLib CoreLibBinder::AttachModule(m_pSystemAssembly->GetManifestModule()); @@ -1839,6 +1837,8 @@ void AppDomain::Create() pDomain->InitVSD(); pDomain->SetStage(AppDomain::STAGE_OPEN); + pDomain->CreateDefaultBinder(); + pDomain.SuppressRelease(); m_pTheAppDomain = pDomain; @@ -2251,7 +2251,7 @@ DispIDCache* AppDomain::SetupRefDispIDCache() #endif // FEATURE_COMINTEROP -FileLoadLock *FileLoadLock::Create(PEFileListLock *pLock, PEFile *pFile, DomainFile *pDomainFile) +FileLoadLock *FileLoadLock::Create(PEFileListLock *pLock, PEAssembly * pPEAssembly, DomainFile *pDomainFile) { CONTRACTL { @@ -2259,12 +2259,12 @@ FileLoadLock *FileLoadLock::Create(PEFileListLock *pLock, PEFile *pFile, DomainF GC_TRIGGERS; MODE_ANY; PRECONDITION(pLock->HasLock()); - PRECONDITION(pLock->FindFileLock(pFile) == NULL); + PRECONDITION(pLock->FindFileLock(pPEAssembly) == NULL); INJECT_FAULT(COMPlusThrowOM();); } CONTRACTL_END; - NewHolder result(new FileLoadLock(pLock, pFile, pDomainFile)); + NewHolder result(new FileLoadLock(pLock, pPEAssembly, pDomainFile)); pLock->AddElement(result); result->AddRef(); // Add one ref on behalf of the ListLock's reference. The corresponding Release() happens in FileLoadLock::CompleteLoadLevel. @@ -2281,7 +2281,7 @@ FileLoadLock::~FileLoadLock() MODE_ANY; } CONTRACTL_END; - ((PEFile *) m_data)->Release(); + ((PEAssembly *) m_data)->Release(); } DomainFile *FileLoadLock::GetDomainFile() @@ -2476,14 +2476,14 @@ UINT32 FileLoadLock::Release() return count; } -FileLoadLock::FileLoadLock(PEFileListLock *pLock, PEFile *pFile, DomainFile *pDomainFile) - : ListLockEntry(pLock, pFile, "File load lock"), +FileLoadLock::FileLoadLock(PEFileListLock *pLock, PEAssembly * pPEAssembly, DomainFile *pDomainFile) + : ListLockEntry(pLock, pPEAssembly, "File load lock"), m_level((FileLoadLevel) (FILE_LOAD_CREATE)), m_pDomainFile(pDomainFile), m_cachedHR(S_OK) { WRAPPER_NO_CONTRACT; - pFile->AddRef(); + pPEAssembly->AddRef(); } void FileLoadLock::HolderLeave(FileLoadLock *pThis) @@ -2531,7 +2531,7 @@ void AppDomain::LoadSystemAssemblies() // // Right now we have only one system assembly. We shouldn't need to add any more. - LoadAssembly(NULL, SystemDomain::System()->SystemFile(), FILE_ACTIVE); + LoadAssembly(NULL, SystemDomain::System()->SystemPEAssembly(), FILE_ACTIVE); } FileLoadLevel AppDomain::GetDomainFileLoadLevel(DomainFile *pFile) @@ -2546,7 +2546,7 @@ FileLoadLevel AppDomain::GetDomainFileLoadLevel(DomainFile *pFile) LoadLockHolder lock(this); - FileLoadLock* pLockEntry = (FileLoadLock *) lock->FindFileLock(pFile->GetFile()); + FileLoadLock* pLockEntry = (FileLoadLock *) lock->FindFileLock(pFile->GetPEAssembly()); if (pLockEntry == NULL) return pFile->GetLoadLevel(); @@ -2576,7 +2576,7 @@ BOOL AppDomain::IsLoading(DomainFile *pFile, FileLoadLevel level) { LoadLockHolder lock(this); - pLock = (FileLoadLock *) lock->FindFileLock(pFile->GetFile()); + pLock = (FileLoadLock *) lock->FindFileLock(pFile->GetPEAssembly()); if (pLock == NULL) { @@ -2613,7 +2613,7 @@ CHECK AppDomain::CheckLoading(DomainFile *pFile, FileLoadLevel level) LoadLockHolder lock(this); - pLock = (FileLoadLock *) lock->FindFileLock(pFile->GetFile()); + pLock = (FileLoadLock *) lock->FindFileLock(pFile->GetPEAssembly()); if (pLock != NULL && pLock->CanAcquire(level)) @@ -2699,7 +2699,7 @@ void AppDomain::LoadDomainFile(DomainFile *pFile, // Load some more if appropriate LoadLockHolder lock(this); - FileLoadLock* pLockEntry = (FileLoadLock *) lock->FindFileLock(pFile->GetFile()); + FileLoadLock* pLockEntry = (FileLoadLock *) lock->FindFileLock(pFile->GetPEAssembly()); if (pLockEntry == NULL) { _ASSERTE (!pFile->IsLoading()); @@ -2731,7 +2731,7 @@ FileLoadLevel AppDomain::GetThreadFileLoadLevel() Assembly *AppDomain::LoadAssembly(AssemblySpec* pIdentity, - PEAssembly *pFile, + PEAssembly * pPEAssembly, FileLoadLevel targetLevel) { CONTRACT(Assembly *) @@ -2739,20 +2739,20 @@ Assembly *AppDomain::LoadAssembly(AssemblySpec* pIdentity, GC_TRIGGERS; THROWS; MODE_ANY; - PRECONDITION(CheckPointer(pFile)); + PRECONDITION(CheckPointer(pPEAssembly)); POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); // May be NULL in recursive load case INJECT_FAULT(COMPlusThrowOM();); } CONTRACT_END; - DomainAssembly *pAssembly = LoadDomainAssembly(pIdentity, pFile, targetLevel); + DomainAssembly *pAssembly = LoadDomainAssembly(pIdentity, pPEAssembly, targetLevel); PREFIX_ASSUME(pAssembly != NULL); RETURN pAssembly->GetAssembly(); } DomainAssembly* AppDomain::LoadDomainAssembly(AssemblySpec* pSpec, - PEAssembly *pFile, + PEAssembly * pPEAssembly, FileLoadLevel targetLevel) { STATIC_CONTRACT_THROWS; @@ -2760,13 +2760,13 @@ DomainAssembly* AppDomain::LoadDomainAssembly(AssemblySpec* pSpec, if (pSpec == nullptr) { // skip caching, since we don't have anything to base it on - return LoadDomainAssemblyInternal(pSpec, pFile, targetLevel); + return LoadDomainAssemblyInternal(pSpec, pPEAssembly, targetLevel); } DomainAssembly* pRetVal = NULL; EX_TRY { - pRetVal = LoadDomainAssemblyInternal(pSpec, pFile, targetLevel); + pRetVal = LoadDomainAssemblyInternal(pSpec, pPEAssembly, targetLevel); } EX_HOOK { @@ -2775,7 +2775,7 @@ DomainAssembly* AppDomain::LoadDomainAssembly(AssemblySpec* pSpec, { // Setup the binder reference in AssemblySpec from the PEAssembly if one is not already set. AssemblyBinder* pCurrentBinder = pSpec->GetBinder(); - AssemblyBinder* pBinderFromPEAssembly = pFile->GetAssemblyBinder(); + AssemblyBinder* pBinderFromPEAssembly = pPEAssembly->GetAssemblyBinder(); if (pCurrentBinder == NULL) { @@ -2811,7 +2811,7 @@ DomainAssembly* AppDomain::LoadDomainAssembly(AssemblySpec* pSpec, DomainAssembly *AppDomain::LoadDomainAssemblyInternal(AssemblySpec* pIdentity, - PEAssembly *pFile, + PEAssembly * pPEAssembly, FileLoadLevel targetLevel) { CONTRACT(DomainAssembly *) @@ -2819,8 +2819,8 @@ DomainAssembly *AppDomain::LoadDomainAssemblyInternal(AssemblySpec* pIdentity, GC_TRIGGERS; THROWS; MODE_ANY; - PRECONDITION(CheckPointer(pFile)); - PRECONDITION(pFile->IsSystem() || ::GetAppDomain()==this); + PRECONDITION(CheckPointer(pPEAssembly)); + PRECONDITION(::GetAppDomain()==this); POSTCONDITION(CheckPointer(RETVAL)); POSTCONDITION(RETVAL->GetLoadLevel() >= GetThreadFileLoadLevel() || RETVAL->GetLoadLevel() >= targetLevel); @@ -2836,16 +2836,16 @@ DomainAssembly *AppDomain::LoadDomainAssemblyInternal(AssemblySpec* pIdentity, GCX_PREEMP(); // Check for existing fully loaded assembly, or for an assembly which has failed during the loading process. - result = FindAssembly(pFile, FindAssemblyOptions_IncludeFailedToLoad); + result = FindAssembly(pPEAssembly, FindAssemblyOptions_IncludeFailedToLoad); if (result == NULL) { LoaderAllocator *pLoaderAllocator = NULL; - AssemblyBinder *pFileBinder = pFile->GetAssemblyBinder(); + AssemblyBinder *pAssemblyBinder = pPEAssembly->GetAssemblyBinder(); // Assemblies loaded with CustomAssemblyBinder need to use a different LoaderAllocator if // marked as collectible - pLoaderAllocator = pFileBinder->GetLoaderAllocator(); + pLoaderAllocator = pAssemblyBinder->GetLoaderAllocator(); if (pLoaderAllocator == NULL) { pLoaderAllocator = this->GetLoaderAllocator(); @@ -2853,22 +2853,22 @@ DomainAssembly *AppDomain::LoadDomainAssemblyInternal(AssemblySpec* pIdentity, // Allocate the DomainAssembly a bit early to avoid GC mode problems. We could potentially avoid // a rare redundant allocation by moving this closer to FileLoadLock::Create, but it's not worth it. - NewHolder pDomainAssembly = new DomainAssembly(this, pFile, pLoaderAllocator); + NewHolder pDomainAssembly = new DomainAssembly(this, pPEAssembly, pLoaderAllocator); LoadLockHolder lock(this); // Find the list lock entry - FileLoadLock * fileLock = (FileLoadLock *)lock->FindFileLock(pFile); + FileLoadLock * fileLock = (FileLoadLock *)lock->FindFileLock(pPEAssembly); bool registerNewAssembly = false; if (fileLock == NULL) { // Check again in case we were racing - result = FindAssembly(pFile, FindAssemblyOptions_IncludeFailedToLoad); + result = FindAssembly(pPEAssembly, FindAssemblyOptions_IncludeFailedToLoad); if (result == NULL) { // We are the first one in - create the DomainAssembly registerNewAssembly = true; - fileLock = FileLoadLock::Create(lock, pFile, pDomainAssembly); + fileLock = FileLoadLock::Create(lock, pPEAssembly, pDomainAssembly); pDomainAssembly.SuppressRelease(); if (pDomainAssembly->IsCollectible()) { @@ -2901,7 +2901,7 @@ DomainAssembly *AppDomain::LoadDomainAssemblyInternal(AssemblySpec* pIdentity, if (registerNewAssembly) { - pFile->GetAssemblyBinder()->AddLoadedAssembly(pDomainAssembly->GetCurrentAssembly()); + pPEAssembly->GetAssemblyBinder()->AddLoadedAssembly(pDomainAssembly->GetCurrentAssembly()); } } else @@ -2915,11 +2915,11 @@ DomainAssembly *AppDomain::LoadDomainAssemblyInternal(AssemblySpec* pIdentity, ThrowHR(COR_E_ASSEMBLYEXPECTED); } - // Cache result in all cases, since found pFile could be from a different AssemblyRef than pIdentity + // Cache result in all cases, since found pPEAssembly could be from a different AssemblyRef than pIdentity if (pIdentity == NULL) { AssemblySpec spec; - spec.InitializeSpec(result->GetFile()); + spec.InitializeSpec(result->GetPEAssembly()); GetAppDomain()->AddAssemblyToCache(&spec, result); } else @@ -2956,26 +2956,6 @@ DomainFile *AppDomain::LoadDomainFile(FileLoadLock *pLock, FileLoadLevel targetL // Make sure we release the lock on exit FileLoadLockRefHolder lockRef(pLock); - // We need to perform the early steps of loading CoreLib without a domain transition. This is - // important for bootstrapping purposes - we need to get CoreLib at least partially loaded - // into a domain before we can run serialization code to do the transition. - // - // Note that we cannot do this in general for all assemblies, because some of the security computations - // require the managed exposed object, which must be created in the correct app domain. - - if (this != GetAppDomain() - && pFile->GetFile()->IsSystem() - && targetLevel > FILE_LOAD_ALLOCATE) - { - // Re-call the routine with a limited load level. This will cause the first part of the load to - // get performed in the current app domain. - - pLock->AddRef(); - LoadDomainFile(pLock, targetLevel > FILE_LOAD_ALLOCATE ? FILE_LOAD_ALLOCATE : targetLevel); - - // Now continue on to complete the rest of the load, if any. - } - // Do a quick out check for the already loaded case. if (pLock->GetLoadLevel() >= targetLevel) { @@ -3151,7 +3131,7 @@ void AppDomain::TryIncrementalLoad(DomainFile *pFile, FileLoadLevel workLevel, F } if (!EEFileLoadException::CheckType(pEx)) - EEFileLoadException::Throw(pFile->GetFile(), pEx->GetHR(), pEx); + EEFileLoadException::Throw(pFile->GetPEAssembly(), pEx->GetHR(), pEx); } // Otherwise, we simply abort this load, and can retry later on. @@ -3212,7 +3192,7 @@ void AppDomain::SetupSharedStatics() SetObjectReference( pEmptyStringHandle, StringObject::GetEmptyString()); } -DomainAssembly * AppDomain::FindAssembly(PEAssembly * pFile, FindAssemblyOptions options/* = FindAssemblyOptions_None*/) +DomainAssembly * AppDomain::FindAssembly(PEAssembly * pPEAssembly, FindAssemblyOptions options/* = FindAssemblyOptions_None*/) { CONTRACTL { @@ -3225,9 +3205,9 @@ DomainAssembly * AppDomain::FindAssembly(PEAssembly * pFile, FindAssemblyOptions const bool includeFailedToLoad = (options & FindAssemblyOptions_IncludeFailedToLoad) != 0; - if (pFile->HasHostAssembly()) + if (pPEAssembly->HasHostAssembly()) { - DomainAssembly * pDA = FindAssembly(pFile->GetHostAssembly()); + DomainAssembly * pDA = FindAssembly(pPEAssembly->GetHostAssembly()); if (pDA != nullptr && (pDA->IsLoaded() || (includeFailedToLoad && pDA->IsError()))) { return pDA; @@ -3243,10 +3223,9 @@ DomainAssembly * AppDomain::FindAssembly(PEAssembly * pFile, FindAssemblyOptions while (i.Next(pDomainAssembly.This())) { - PEFile * pManifestFile = pDomainAssembly->GetFile(); + PEAssembly * pManifestFile = pDomainAssembly->GetPEAssembly(); if (pManifestFile && - !pManifestFile->IsResource() && - pManifestFile->Equals(pFile)) + pManifestFile->Equals(pPEAssembly)) { // Caller already has PEAssembly, so we can give DomainAssembly away freely without added reference return pDomainAssembly.GetValue(); @@ -3412,7 +3391,7 @@ PVOID AppDomain::GetFriendlyNameNoSet(bool* isUtf8) #ifndef DACCESS_COMPILE -BOOL AppDomain::AddFileToCache(AssemblySpec* pSpec, PEAssembly *pFile, BOOL fAllowFailure) +BOOL AppDomain::AddFileToCache(AssemblySpec* pSpec, PEAssembly * pPEAssembly, BOOL fAllowFailure) { CONTRACTL { @@ -3428,7 +3407,7 @@ BOOL AppDomain::AddFileToCache(AssemblySpec* pSpec, PEAssembly *pFile, BOOL fAll DomainCacheCrstHolderForGCCoop holder(this); // !!! suppress exceptions - if(!m_AssemblyCache.StoreFile(pSpec, pFile) && !fAllowFailure) + if(!m_AssemblyCache.StorePEAssembly(pSpec, pPEAssembly) && !fAllowFailure) { // TODO: Disabling the below assertion as currently we experience // inconsistency on resolving the Microsoft.Office.Interop.MSProject.dll @@ -3538,17 +3517,17 @@ NATIVE_LIBRARY_HANDLE AppDomain::FindUnmanagedImageInCache(LPCWSTR libraryName) RETURN existingEntry->Handle; } -BOOL AppDomain::RemoveFileFromCache(PEAssembly *pFile) +BOOL AppDomain::RemoveFileFromCache(PEAssembly * pPEAssembly) { CONTRACTL { GC_TRIGGERS; - PRECONDITION(CheckPointer(pFile)); + PRECONDITION(CheckPointer(pPEAssembly)); } CONTRACTL_END; LoadLockHolder lock(this); - FileLoadLock *fileLock = (FileLoadLock *)lock->FindFileLock(pFile); + FileLoadLock *fileLock = (FileLoadLock *)lock->FindFileLock(pPEAssembly); if (fileLock == NULL) return FALSE; @@ -3611,9 +3590,9 @@ PEAssembly* AppDomain::FindCachedFile(AssemblySpec* pSpec, BOOL fThrow /*=TRUE*/ if (fThrow && pSpec->IsCoreLib()) { CONSISTENCY_CHECK(SystemDomain::System()->SystemAssembly() != NULL); - PEAssembly *pFile = SystemDomain::System()->SystemFile(); - pFile->AddRef(); - return pFile; + PEAssembly * pPEAssembly = SystemDomain::System()->SystemPEAssembly(); + pPEAssembly->AddRef(); + return pPEAssembly; } return m_AssemblyCache.LookupFile(pSpec, fThrow); @@ -3697,16 +3676,16 @@ PEAssembly * AppDomain::BindAssemblySpec( if (boundAssembly) { - if (SystemDomain::SystemFile() && boundAssembly->GetAssemblyName()->IsCoreLib()) + if (SystemDomain::SystemPEAssembly() && boundAssembly->GetAssemblyName()->IsCoreLib()) { // Avoid rebinding to another copy of CoreLib - result = SystemDomain::SystemFile(); + result = SystemDomain::SystemPEAssembly(); result.SuppressRelease(); // Didn't get a refcount } else { - // IsSystem on the PEFile should be false, even for CoreLib satellites - result = PEAssembly::Open(boundAssembly, FALSE); + // IsSystem on the PEAssembly should be false, even for CoreLib satellites + result = PEAssembly::Open(boundAssembly); } // Setup the reference to the binder, which performed the bind, into the AssemblySpec @@ -3861,9 +3840,9 @@ PEAssembly *AppDomain::TryResolveAssemblyUsingEvent(AssemblySpec *pSpec) Assembly *pAssembly = RaiseAssemblyResolveEvent(pSpec); if (pAssembly != nullptr) { - PEAssembly *pFile = pAssembly->GetManifestFile(); - pFile->AddRef(); - result = pFile; + PEAssembly* pPEAssembly = pAssembly->GetManifestFile(); + pPEAssembly->AddRef(); + result = pPEAssembly; } BinderTracing::ResolutionAttemptedOperation::TraceAppDomainAssemblyResolve(pSpec, result); @@ -3924,7 +3903,7 @@ void AppDomain::RaiseLoadingAssemblyEvent(DomainAssembly *pAssembly) } CONTRACTL_END; - if (pAssembly->GetFile()->IsSystem()) + if (pAssembly->GetPEAssembly()->IsSystem()) { return; } @@ -4040,7 +4019,7 @@ AppDomain::RaiseUnhandledExceptionEvent(OBJECTREF *pThrowable, BOOL isTerminatin } -DefaultAssemblyBinder *AppDomain::CreateBinderContext() +DefaultAssemblyBinder *AppDomain::CreateDefaultBinder() { CONTRACT(DefaultAssemblyBinder *) { @@ -5138,7 +5117,7 @@ HRESULT RuntimeInvokeHostAssemblyResolver(INT_PTR pManagedAssemblyLoadContextToB } else { - pLoadedPEAssembly = pDomainAssembly->GetFile(); + pLoadedPEAssembly = pDomainAssembly->GetPEAssembly(); if (!pLoadedPEAssembly->HasHostAssembly()) { // Reflection emitted assemblies will not have a domain assembly. @@ -5300,9 +5279,9 @@ SystemDomain::EnumMemoryRegions(CLRDataEnumMemoryFlags flags, } BaseDomain::EnumMemoryRegions(flags, false); - if (m_pSystemFile.IsValid()) + if (m_pSystemPEAssembly.IsValid()) { - m_pSystemFile->EnumMemoryRegions(flags); + m_pSystemPEAssembly->EnumMemoryRegions(flags); } if (m_pSystemAssembly.IsValid()) { @@ -5372,11 +5351,11 @@ void AppDomain::PublishHostedAssembly( } CONTRACTL_END - if (pDomainAssembly->GetFile()->HasHostAssembly()) + if (pDomainAssembly->GetPEAssembly()->HasHostAssembly()) { // We have to serialize all Add operations CrstHolder lockAdd(&m_crstHostAssemblyMapAdd); - _ASSERTE(m_hostAssemblyMap.Lookup(pDomainAssembly->GetFile()->GetHostAssembly()) == nullptr); + _ASSERTE(m_hostAssemblyMap.Lookup(pDomainAssembly->GetPEAssembly()->GetHostAssembly()) == nullptr); // Wrapper for m_hostAssemblyMap.Add that avoids call out into host HostAssemblyMap::AddPhases addCall; @@ -5401,75 +5380,6 @@ void AppDomain::PublishHostedAssembly( } } -//--------------------------------------------------------------------------------------------------------------------- -void AppDomain::UpdatePublishHostedAssembly( - DomainAssembly * pAssembly, - PTR_PEFile pFile) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - CAN_TAKE_LOCK; - } - CONTRACTL_END - - if (pAssembly->GetFile()->HasHostAssembly()) - { - // We have to serialize all Add operations - CrstHolder lockAdd(&m_crstHostAssemblyMapAdd); - { - // Wrapper for m_hostAssemblyMap.Add that avoids call out into host - OriginalFileHostAssemblyMap::AddPhases addCall; - bool fAddOrigFile = false; - - // For cases where the pefile is being updated - // 1. Preallocate one element - if (pFile != pAssembly->GetFile()) - { - addCall.PreallocateForAdd(&m_hostAssemblyMapForOrigFile); - fAddOrigFile = true; - } - - { - // We cannot call out into host from ForbidSuspend region (i.e. no allocations/deallocations) - ForbidSuspendThreadHolder suspend; - { - CrstHolder lock(&m_crstHostAssemblyMap); - - // Remove from hash table. - _ASSERTE(m_hostAssemblyMap.Lookup(pAssembly->GetFile()->GetHostAssembly()) != nullptr); - m_hostAssemblyMap.Remove(pAssembly->GetFile()->GetHostAssembly()); - - // Update PEFile on DomainAssembly. (This may cause the key for the hash to change, which is why we need this function) - pAssembly->UpdatePEFileWorker(pFile); - - _ASSERTE(fAddOrigFile == (pAssembly->GetOriginalFile() != pAssembly->GetFile())); - if (fAddOrigFile) - { - // Add to the orig file hash table if we might be in a case where we've cached the original pefile and not the final pe file (for use during GetAssemblyIfLoaded) - addCall.Add(pAssembly); - } - - // Add back to the hashtable (the call to Remove above guarantees that we will not call into host for table reallocation) - _ASSERTE(m_hostAssemblyMap.Lookup(pAssembly->GetFile()->GetHostAssembly()) == nullptr); - m_hostAssemblyMap.Add(pAssembly); - } - } - - // 4. Cleanup the old memory (if any) - if (fAddOrigFile) - addCall.DeleteOldTable(); - } - } - else - { - - pAssembly->UpdatePEFileWorker(pFile); - } -} - //--------------------------------------------------------------------------------------------------------------------- void AppDomain::UnPublishHostedAssembly( DomainAssembly * pAssembly) @@ -5483,19 +5393,13 @@ void AppDomain::UnPublishHostedAssembly( } CONTRACTL_END - if (pAssembly->GetFile()->HasHostAssembly()) + if (pAssembly->GetPEAssembly()->HasHostAssembly()) { ForbidSuspendThreadHolder suspend; { CrstHolder lock(&m_crstHostAssemblyMap); - _ASSERTE(m_hostAssemblyMap.Lookup(pAssembly->GetFile()->GetHostAssembly()) != nullptr); - m_hostAssemblyMap.Remove(pAssembly->GetFile()->GetHostAssembly()); - - // We also have an entry in m_hostAssemblyMapForOrigFile. Handle that case. - if (pAssembly->GetOriginalFile() != pAssembly->GetFile()) - { - m_hostAssemblyMapForOrigFile.Remove(pAssembly->GetOriginalFile()->GetHostAssembly()); - } + _ASSERTE(m_hostAssemblyMap.Lookup(pAssembly->GetPEAssembly()->GetHostAssembly()) != nullptr); + m_hostAssemblyMap.Remove(pAssembly->GetPEAssembly()->GetHostAssembly()); } } } @@ -5521,18 +5425,7 @@ PTR_DomainAssembly AppDomain::FindAssembly(PTR_BINDER_SPACE_Assembly pHostAssemb ForbidSuspendThreadHolder suspend; { CrstHolder lock(&m_crstHostAssemblyMap); - PTR_DomainAssembly returnValue = m_hostAssemblyMap.Lookup(pHostAssembly); - if (returnValue == NULL) - { - // If not found in the m_hostAssemblyMap, look in the m_hostAssemblyMapForOrigFile - // This is necessary as it may happen during in a second AppDomain that the PEFile - // first discovered in the AppDomain may not be used by the DomainFile, but the CLRPrivBinderFusion - // will in some cases find the pHostAssembly associated with this no longer used PEFile - // instead of the PEFile that was finally decided upon. - returnValue = m_hostAssemblyMapForOrigFile.Lookup(pHostAssembly); - } - - return returnValue; + return m_hostAssemblyMap.Lookup(pHostAssembly); } } } diff --git a/src/coreclr/vm/appdomain.hpp b/src/coreclr/vm/appdomain.hpp index d31dc9af6d8ab..27509c601e4cc 100644 --- a/src/coreclr/vm/appdomain.hpp +++ b/src/coreclr/vm/appdomain.hpp @@ -700,7 +700,7 @@ class PEFileListLock : public ListLock { public: #ifndef DACCESS_COMPILE - ListLockEntry *FindFileLock(PEFile *pFile) + ListLockEntry *FindFileLock(PEAssembly *pPEAssembly) { STATIC_CONTRACT_NOTHROW; STATIC_CONTRACT_GC_NOTRIGGER; @@ -714,7 +714,7 @@ class PEFileListLock : public ListLock pEntry != NULL; pEntry = pEntry->m_pNext) { - if (((PEFile *)pEntry->m_data)->Equals(pFile)) + if (((PEAssembly *)pEntry->m_data)->Equals(pPEAssembly)) { return pEntry; } @@ -777,7 +777,7 @@ class FileLoadLock : public ListLockEntry HRESULT m_cachedHR; public: - static FileLoadLock *Create(PEFileListLock *pLock, PEFile *pFile, DomainFile *pDomainFile); + static FileLoadLock *Create(PEFileListLock *pLock, PEAssembly *pPEAssembly, DomainFile *pDomainFile); ~FileLoadLock(); DomainFile *GetDomainFile(); @@ -807,7 +807,7 @@ class FileLoadLock : public ListLockEntry private: - FileLoadLock(PEFileListLock *pLock, PEFile *pFile, DomainFile *pDomainFile); + FileLoadLock(PEFileListLock *pLock, PEAssembly *pPEAssembly, DomainFile *pDomainFile); static void HolderLeave(FileLoadLock *pThis); @@ -1812,11 +1812,11 @@ class AppDomain : public BaseDomain FindAssemblyOptions_IncludeFailedToLoad = 0x1 }; - DomainAssembly * FindAssembly(PEAssembly * pFile, FindAssemblyOptions options = FindAssemblyOptions_None) DAC_EMPTY_RET(NULL); + DomainAssembly * FindAssembly(PEAssembly* pPEAssembly, FindAssemblyOptions options = FindAssemblyOptions_None) DAC_EMPTY_RET(NULL); Assembly *LoadAssembly(AssemblySpec* pIdentity, - PEAssembly *pFile, + PEAssembly *pPEAssembly, FileLoadLevel targetLevel); // this function does not provide caching, you must use LoadDomainAssembly @@ -1826,11 +1826,11 @@ class AppDomain : public BaseDomain // resulting in multiple DomainAssembly objects that share the same PEAssembly for ngen image //which is violating our internal assumptions DomainAssembly *LoadDomainAssemblyInternal( AssemblySpec* pIdentity, - PEAssembly *pFile, + PEAssembly *pPEAssembly, FileLoadLevel targetLevel); DomainAssembly *LoadDomainAssembly( AssemblySpec* pIdentity, - PEAssembly *pFile, + PEAssembly *pPEAssembly, FileLoadLevel targetLevel); @@ -1859,8 +1859,8 @@ class AppDomain : public BaseDomain BOOL IsCached(AssemblySpec *pSpec); #endif // DACCESS_COMPILE - BOOL AddFileToCache(AssemblySpec* pSpec, PEAssembly *pFile, BOOL fAllowFailure = FALSE); - BOOL RemoveFileFromCache(PEAssembly *pFile); + BOOL AddFileToCache(AssemblySpec* pSpec, PEAssembly *pPEAssembly, BOOL fAllowFailure = FALSE); + BOOL RemoveFileFromCache(PEAssembly *pPEAssembly); BOOL AddAssemblyToCache(AssemblySpec* pSpec, DomainAssembly *pAssembly); BOOL RemoveAssemblyFromCache(DomainAssembly* pAssembly); @@ -1974,7 +1974,7 @@ class AppDomain : public BaseDomain return m_tpIndex; } - DefaultAssemblyBinder *CreateBinderContext(); + DefaultAssemblyBinder *CreateDefaultBinder(); void SetIgnoreUnhandledExceptions() { @@ -2364,7 +2364,7 @@ class AppDomain : public BaseDomain //----------------------------------------------------------- // Static BINDER_SPACE::Assembly -> DomainAssembly mapping functions. // This map does not maintain a reference count to either key or value. - // PEFile maintains a reference count on the BINDER_SPACE::Assembly through its code:PEFile::m_pHostAssembly field. + // PEAssembly maintains a reference count on the BINDER_SPACE::Assembly through its code:PEAssembly::m_pHostAssembly field. // It is removed from this hash table by code:DomainAssembly::~DomainAssembly. struct HostAssemblyHashTraits : public DefaultSHashTraits { @@ -2374,7 +2374,7 @@ class AppDomain : public BaseDomain static key_t GetKey(element_t const & elem) { STATIC_CONTRACT_WRAPPER; - return elem->GetFile()->GetHostAssembly(); + return elem->GetPEAssembly()->GetHostAssembly(); } static BOOL Equals(key_t key1, key_t key2) @@ -2396,20 +2396,8 @@ class AppDomain : public BaseDomain static bool IsDeleted(const element_t & e) { return dac_cast(e) == (TADDR)-1; } }; - struct OriginalFileHostAssemblyHashTraits : public HostAssemblyHashTraits - { - public: - static key_t GetKey(element_t const & elem) - { - STATIC_CONTRACT_WRAPPER; - return elem->GetOriginalFile()->GetHostAssembly(); - } - }; - typedef SHash HostAssemblyMap; - typedef SHash OriginalFileHostAssemblyMap; HostAssemblyMap m_hostAssemblyMap; - OriginalFileHostAssemblyMap m_hostAssemblyMapForOrigFile; CrstExplicitInit m_crstHostAssemblyMap; // Lock to serialize all Add operations (in addition to the "read-lock" above) CrstExplicitInit m_crstHostAssemblyMapAdd; @@ -2427,11 +2415,6 @@ class AppDomain : public BaseDomain void PublishHostedAssembly( DomainAssembly* pAssembly); - // Called from DomainAssembly::UpdatePEFile. - void UpdatePublishHostedAssembly( - DomainAssembly* pAssembly, - PTR_PEFile pFile); - // Called from DomainAssembly::~DomainAssembly void UnPublishHostedAssembly( DomainAssembly* pAssembly); @@ -2512,12 +2495,12 @@ class SystemDomain : public BaseDomain return m_pSystemDomain; } - static PEAssembly* SystemFile() + static PEAssembly* SystemPEAssembly() { WRAPPER_NO_CONTRACT; _ASSERTE(m_pSystemDomain); - return System()->m_pSystemFile; + return System()->m_pSystemPEAssembly; } static Assembly* SystemAssembly() @@ -2708,7 +2691,7 @@ class SystemDomain : public BaseDomain } #endif - PTR_PEAssembly m_pSystemFile; // Single assembly (here for quicker reference); + PTR_PEAssembly m_pSystemPEAssembly;// Single assembly (here for quicker reference); PTR_Assembly m_pSystemAssembly; // Single assembly (here for quicker reference); GlobalLoaderAllocator m_GlobalAllocator; diff --git a/src/coreclr/vm/assembly.cpp b/src/coreclr/vm/assembly.cpp index c0fe297fc1ecc..754cf91696ff7 100644 --- a/src/coreclr/vm/assembly.cpp +++ b/src/coreclr/vm/assembly.cpp @@ -62,9 +62,56 @@ #ifndef DACCESS_COMPILE volatile uint32_t g_cAssemblies = 0; - static CrstStatic g_friendAssembliesCrst; +namespace +{ + void DefineEmitScope(GUID iid, void** ppEmit) + { + CONTRACT_VOID + { + PRECONDITION(CheckPointer(ppEmit)); + POSTCONDITION(CheckPointer(*ppEmit)); + THROWS; + GC_TRIGGERS; + MODE_ANY; + INJECT_FAULT(COMPlusThrowOM();); + } + CONTRACT_END; + + SafeComHolder pDispenser; + + // Get the Dispenser interface. + MetaDataGetDispenser( + CLSID_CorMetaDataDispenser, + IID_IMetaDataDispenserEx, + (void**)&pDispenser); + if (pDispenser == NULL) + { + ThrowOutOfMemory(); + } + + // Set the option on the dispenser turn on duplicate check for TypeDef and moduleRef + VARIANT varOption; + V_VT(&varOption) = VT_UI4; + V_I4(&varOption) = MDDupDefault | MDDupTypeDef | MDDupModuleRef | MDDupExportedType | MDDupAssemblyRef | MDDupPermission | MDDupFile; + IfFailThrow(pDispenser->SetOption(MetaDataCheckDuplicatesFor, &varOption)); + + // Set minimal MetaData size + V_VT(&varOption) = VT_UI4; + V_I4(&varOption) = MDInitialSizeMinimal; + IfFailThrow(pDispenser->SetOption(MetaDataInitialSize, &varOption)); + + // turn on the thread safety! + V_I4(&varOption) = MDThreadSafetyOn; + IfFailThrow(pDispenser->SetOption(MetaDataThreadSafetyOptions, &varOption)); + + IfFailThrow(pDispenser->DefineScope(CLSID_CorMetaDataRuntime, 0, iid, (IUnknown**)ppEmit)); + + RETURN; + } +} + void Assembly::Initialize() { g_friendAssembliesCrst.Init(CrstLeafLock); @@ -75,12 +122,12 @@ void Assembly::Initialize() // It cannot do any allocations or operations that might fail. Those operations should be done // in Assembly::Init() //---------------------------------------------------------------------------------------------- -Assembly::Assembly(BaseDomain *pDomain, PEAssembly* pFile, DebuggerAssemblyControlFlags debuggerFlags, BOOL fIsCollectible) : +Assembly::Assembly(BaseDomain *pDomain, PEAssembly* pPEAssembly, DebuggerAssemblyControlFlags debuggerFlags, BOOL fIsCollectible) : m_pDomain(pDomain), m_pClassLoader(NULL), m_pEntryPoint(NULL), m_pManifest(NULL), - m_pManifestFile(clr::SafeAddRef(pFile)), + m_pManifestFile(clr::SafeAddRef(pPEAssembly)), m_pFriendAssemblyDescriptor(NULL), m_isDynamic(false), #ifdef FEATURE_COLLECTIBLE_TYPES @@ -164,7 +211,7 @@ void Assembly::Init(AllocMemTracker *pamTracker, LoaderAllocator *pLoaderAllocat if (IsCollectible()) { COUNT_T size; - BYTE *start = (BYTE*)m_pManifest->GetFile()->GetLoadedImageContents(&size); + BYTE *start = (BYTE*)m_pManifest->GetPEAssembly()->GetLoadedImageContents(&size); if (start != NULL) { GCX_COOP(); @@ -381,14 +428,14 @@ Assembly *Assembly::CreateDynamic(AppDomain *pDomain, AssemblyBinder* pBinder, C // Set up the assembly manifest metadata // When we create dynamic assembly, we always use a working copy of IMetaDataAssemblyEmit // to store temporary runtime assembly information. This is to preserve the invariant that - // an assembly must have a PEFile with proper metadata. + // an assembly must have a PEAssembly with proper metadata. // This working copy of IMetaDataAssemblyEmit will store every AssemblyRef as a simple name // reference as we must have an instance of Assembly(can be dynamic assembly) before we can // add such a reference. Also because the referenced assembly if dynamic strong name, it may // not be ready to be hashed! SafeComHolder pAssemblyEmit; - PEFile::DefineEmitScope( + DefineEmitScope( IID_IMetaDataAssemblyEmit, &pAssemblyEmit); @@ -453,7 +500,7 @@ Assembly *Assembly::CreateDynamic(AppDomain *pDomain, AssemblyBinder* pBinder, C DWORD dwFlags = args->assemblyName->GetFlags(); // Now create a dynamic PE file out of the name & metadata - PEAssemblyHolder pFile; + PEAssemblyHolder pPEAssembly; { GCX_PREEMP(); @@ -462,7 +509,7 @@ Assembly *Assembly::CreateDynamic(AppDomain *pDomain, AssemblyBinder* pBinder, C IfFailThrow(pAssemblyEmit->DefineAssembly(publicKey, publicKey.GetSize(), ulHashAlgId, name, &assemData, dwFlags, &ma)); - pFile = PEAssembly::Create(pCallerAssembly->GetManifestFile(), pAssemblyEmit); + pPEAssembly = PEAssembly::Create(pCallerAssembly->GetManifestFile(), pAssemblyEmit); AssemblyBinder* pFallbackBinder = pBinder; @@ -477,7 +524,7 @@ Assembly *Assembly::CreateDynamic(AppDomain *pDomain, AssemblyBinder* pBinder, C // and will have a fallback load context binder associated with it. // There is always a manifest file - wehther working with static or dynamic assemblies. - PEFile* pCallerAssemblyManifestFile = pCallerAssembly->GetManifestFile(); + PEAssembly* pCallerAssemblyManifestFile = pCallerAssembly->GetManifestFile(); _ASSERTE(pCallerAssemblyManifestFile != NULL); if (!pCallerAssemblyManifestFile->IsDynamic()) @@ -503,7 +550,7 @@ Assembly *Assembly::CreateDynamic(AppDomain *pDomain, AssemblyBinder* pBinder, C _ASSERTE(pFallbackBinder != nullptr); // Set it as the fallback load context binder for the dynamic assembly being created - pFile->SetFallbackBinder(pFallbackBinder); + pPEAssembly->SetFallbackBinder(pFallbackBinder); } NewHolder pDomainAssembly; @@ -551,7 +598,7 @@ Assembly *Assembly::CreateDynamic(AppDomain *pDomain, AssemblyBinder* pBinder, C } // Create a domain assembly - pDomainAssembly = new DomainAssembly(pDomain, pFile, pLoaderAllocator); + pDomainAssembly = new DomainAssembly(pDomain, pPEAssembly, pLoaderAllocator); if (pDomainAssembly->IsCollectible()) { // We add the assembly to the LoaderAllocator only when we are sure that it can be added @@ -569,7 +616,7 @@ Assembly *Assembly::CreateDynamic(AppDomain *pDomain, AssemblyBinder* pBinder, C { GCX_PREEMP(); // Assembly::Create will call SuppressRelease on the NewHolder that holds the LoaderAllocator when it transfers ownership - pAssem = Assembly::Create(pDomain, pFile, pDomainAssembly->GetDebuggerInfoBits(), pLoaderAllocator->IsCollectible(), pamTracker, pLoaderAllocator); + pAssem = Assembly::Create(pDomain, pPEAssembly, pDomainAssembly->GetDebuggerInfoBits(), pLoaderAllocator->IsCollectible(), pamTracker, pLoaderAllocator); ReflectionModule* pModule = (ReflectionModule*) pAssem->GetManifestModule(); pModule->SetCreatingAssembly( pCallerAssembly ); @@ -817,7 +864,7 @@ Module *Assembly::FindModuleByExportedType(mdExportedType mdType, // Note that we don't want to attempt a LoadModule if a GetModuleIfLoaded will // succeed, because it has a stronger contract. - Module *pModule = GetManifestModule()->GetModuleIfLoaded(mdLinkRef, TRUE, FALSE); + Module *pModule = GetManifestModule()->GetModuleIfLoaded(mdLinkRef); #ifdef DACCESS_COMPILE return pModule; #else @@ -830,7 +877,11 @@ Module *Assembly::FindModuleByExportedType(mdExportedType mdType, // We should never get here in the GC case - the above should have succeeded. CONSISTENCY_CHECK(!FORBIDGC_LOADER_USE_ENABLED()); - DomainFile * pDomainModule = GetManifestModule()->LoadModule(::GetAppDomain(), mdLinkRef, FALSE, loadFlag!=Loader::Load); + DomainFile* pDomainModule = NULL; + if (loadFlag == Loader::Load) + { + pDomainModule = GetManifestModule()->LoadModule(::GetAppDomain(), mdLinkRef); + } if (pDomainModule == NULL) RETURN NULL; @@ -953,18 +1004,18 @@ Module * Assembly::FindModuleByTypeRef( // Either we're not supposed to load, or we're doing a GC or stackwalk // in which case we shouldn't need to load. So just look up the module // and return what we find. - RETURN(pModule->LookupModule(tkType,FALSE)); + RETURN(pModule->LookupModule(tkType)); } #ifndef DACCESS_COMPILE - DomainFile * pActualDomainFile = pModule->LoadModule(::GetAppDomain(), tkType, FALSE, loadFlag!=Loader::Load); - if (pActualDomainFile == NULL) + if (loadFlag == Loader::Load) { - RETURN NULL; + DomainFile* pActualDomainFile = pModule->LoadModule(::GetAppDomain(), tkType); + RETURN(pActualDomainFile->GetModule()); } else { - RETURN(pActualDomainFile->GetModule()); + RETURN NULL; } #else //DACCESS_COMPILE @@ -1056,7 +1107,7 @@ Module *Assembly::FindModuleByName(LPCSTR pszModuleName) ThrowHR(COR_E_UNAUTHORIZEDACCESS); if (this == SystemDomain::SystemAssembly()) - RETURN m_pManifest->GetModuleIfLoaded(kFile, TRUE, TRUE); + RETURN m_pManifest->GetModuleIfLoaded(kFile); else RETURN m_pManifest->LoadModule(::GetAppDomain(), kFile)->GetModule(); } @@ -1124,7 +1175,7 @@ void Assembly::PrepareModuleForAssembly(Module* module, AllocMemTracker *pamTrac module->SetDebuggerInfoBits(GetDebuggerInfoBits()); LOG((LF_CORDB, LL_INFO10, "Module %s: bits=0x%x\n", - module->GetFile()->GetSimpleName(), + module->GetPEAssembly()->GetSimpleName(), module->GetDebuggerInfoBits())); #endif // DEBUGGING_SUPPORTED @@ -1648,7 +1699,7 @@ MethodDesc* Assembly::GetEntryPoint() Module *pModule = NULL; switch(TypeFromToken(mdEntry)) { case mdtFile: - pModule = m_pManifest->LoadModule(::GetAppDomain(), mdEntry, FALSE)->GetModule(); + pModule = m_pManifest->LoadModule(::GetAppDomain(), mdEntry)->GetModule(); mdEntry = pModule->GetEntryPointToken(); if ( (TypeFromToken(mdEntry) != mdtMethodDef) || @@ -1657,7 +1708,7 @@ MethodDesc* Assembly::GetEntryPoint() break; case mdtMethodDef: - if (m_pManifestFile->GetPersistentMDImport()->IsValidToken(mdEntry)) + if (m_pManifestFile->GetMDImport()->IsValidToken(mdEntry)) pModule = m_pManifest; break; } @@ -2240,7 +2291,7 @@ ReleaseHolder FriendAssemblyDescriptor::CreateFriendAs ReleaseHolder pFriendAssemblies = new FriendAssemblyDescriptor; // We're going to do this twice, once for InternalsVisibleTo and once for IgnoresAccessChecks - ReleaseHolder pImport(pAssembly->GetMDImportWithRef()); + IMDInternalImport* pImport = pAssembly->GetMDImport(); for(int count = 0 ; count < 2 ; ++count) { _ASSERTE(pImport != NULL); diff --git a/src/coreclr/vm/assembly.hpp b/src/coreclr/vm/assembly.hpp index db972f0ccf149..f8de5c162e752 100644 --- a/src/coreclr/vm/assembly.hpp +++ b/src/coreclr/vm/assembly.hpp @@ -79,13 +79,13 @@ class Assembly friend class ClrDataAccess; public: - Assembly(BaseDomain *pDomain, PEAssembly *pFile, DebuggerAssemblyControlFlags debuggerFlags, BOOL fIsCollectible); + Assembly(BaseDomain *pDomain, PEAssembly *pPEAssembly, DebuggerAssemblyControlFlags debuggerFlags, BOOL fIsCollectible); void Init(AllocMemTracker *pamTracker, LoaderAllocator *pLoaderAllocator); void StartUnload(); void Terminate( BOOL signalProfiler = TRUE ); - static Assembly *Create(BaseDomain *pDomain, PEAssembly *pFile, DebuggerAssemblyControlFlags debuggerFlags, BOOL fIsCollectible, AllocMemTracker *pamTracker, LoaderAllocator *pLoaderAllocator); + static Assembly *Create(BaseDomain *pDomain, PEAssembly *pPEAssembly, DebuggerAssemblyControlFlags debuggerFlags, BOOL fIsCollectible, AllocMemTracker *pamTracker, LoaderAllocator *pLoaderAllocator); static void Initialize(); BOOL IsSystem() { WRAPPER_NO_CONTRACT; return m_pManifestFile->IsSystem(); } @@ -299,7 +299,7 @@ class Assembly { WRAPPER_NO_CONTRACT; SUPPORTS_DAC; - return m_pManifestFile->GetPersistentMDImport(); + return m_pManifestFile->GetMDImport(); } HRESULT GetCustomAttribute(mdToken parentToken, @@ -312,14 +312,6 @@ class Assembly return GetManifestModule()->GetCustomAttribute(parentToken, attribute, ppData, pcbData); } -#ifndef DACCESS_COMPILE - IMetaDataAssemblyImport* GetManifestAssemblyImporter() - { - WRAPPER_NO_CONTRACT; - return m_pManifestFile->GetAssemblyImporter(); - } -#endif // DACCESS_COMPILE - mdAssembly GetManifestToken() { LIMITED_METHOD_CONTRACT; diff --git a/src/coreclr/vm/assemblybinder.cpp b/src/coreclr/vm/assemblybinder.cpp index 1ce6d2e93beb1..2e231741efbd3 100644 --- a/src/coreclr/vm/assemblybinder.cpp +++ b/src/coreclr/vm/assemblybinder.cpp @@ -32,7 +32,7 @@ NativeImage* AssemblyBinder::LoadNativeImage(Module* componentModule, LPCUTF8 na STANDARD_VM_CONTRACT; BaseDomain::LoadLockHolder lock(AppDomain::GetCurrentDomain()); - AssemblyBinder* binder = componentModule->GetFile()->GetAssemblyBinder(); + AssemblyBinder* binder = componentModule->GetPEAssembly()->GetAssemblyBinder(); PTR_LoaderAllocator moduleLoaderAllocator = componentModule->GetLoaderAllocator(); bool isNewNativeImage; diff --git a/src/coreclr/vm/assemblyname.cpp b/src/coreclr/vm/assemblyname.cpp index 0983c80715fae..c04a01408bbf8 100644 --- a/src/coreclr/vm/assemblyname.cpp +++ b/src/coreclr/vm/assemblyname.cpp @@ -56,7 +56,7 @@ FCIMPL1(Object*, AssemblyNameNative::GetFileInformation, StringObject* filenameU // waiting for it to happen during HasNTHeaders. This allows us to // get the assembly name for images that contain native code for a // non-native platform. - PEImageLayoutHolder pLayout(pImage->GetLayout(PEImageLayout::LAYOUT_FLAT, PEImage::LAYOUT_CREATEIFNEEDED)); + PEImageLayout* pLayout = pImage->GetOrCreateLayout(PEImageLayout::LAYOUT_FLAT); pImage->VerifyIsAssembly(); diff --git a/src/coreclr/vm/assemblynative.cpp b/src/coreclr/vm/assemblynative.cpp index 37d27fb1a9c25..a67c9e5c4c0b6 100644 --- a/src/coreclr/vm/assemblynative.cpp +++ b/src/coreclr/vm/assemblynative.cpp @@ -108,7 +108,7 @@ void QCALLTYPE AssemblyNative::InternalLoad(QCall::ObjectHandleOnStack assemblyN { // If the requesting assembly has Fallback LoadContext binder available, // then set it up in the AssemblySpec. - PEFile *pRefAssemblyManifestFile = pRefAssembly->GetManifestFile(); + PEAssembly *pRefAssemblyManifestFile = pRefAssembly->GetManifestFile(); spec.SetFallbackBinderForRequestingAssembly(pRefAssemblyManifestFile->GetFallbackBinder()); } @@ -157,7 +157,7 @@ Assembly* AssemblyNative::LoadFromPEImage(AssemblyBinder* pBinder, PEImage *pIma // Set the caller's assembly to be CoreLib DomainAssembly *pCallersAssembly = SystemDomain::System()->SystemAssembly()->GetDomainAssembly(); - PEAssembly *pParentAssembly = pCallersAssembly->GetFile(); + PEAssembly *pParentAssembly = pCallersAssembly->GetPEAssembly(); // Initialize the AssemblySpec AssemblySpec spec; @@ -289,7 +289,7 @@ void QCALLTYPE AssemblyNative::LoadFromStream(INT_PTR ptrNativeAssemblyBinder, I // we created above. We need pointer comparison instead of pe image equivalence // to avoid mixed binaries/PDB pairs of other images. // This applies to both Desktop CLR and CoreCLR, with or without fusion. - BOOL fIsSameAssembly = (pLoadedAssembly->GetManifestFile()->GetILimage() == pILImage); + BOOL fIsSameAssembly = (pLoadedAssembly->GetManifestFile()->GetPEImage() == pILImage); // Setting the PDB info is only applicable for our original assembly. // This applies to both Desktop CLR and CoreCLR, with or without fusion. @@ -351,7 +351,7 @@ void QCALLTYPE AssemblyNative::GetLocation(QCall::AssemblyHandle pAssembly, QCal BEGIN_QCALL; { - retString.Set(pAssembly->GetFile()->GetPath()); + retString.Set(pAssembly->GetPEAssembly()->GetPath()); } END_QCALL; @@ -449,7 +449,7 @@ FCIMPL1(FC_BOOL_RET, AssemblyNative::IsDynamic, AssemblyBaseObject* pAssemblyUNS if (refAssembly == NULL) FCThrowRes(kArgumentNullException, W("Arg_InvalidHandle")); - FC_RETURN_BOOL(refAssembly->GetDomainAssembly()->GetFile()->IsDynamic()); + FC_RETURN_BOOL(refAssembly->GetDomainAssembly()->GetPEAssembly()->IsDynamic()); } FCIMPLEND @@ -461,7 +461,7 @@ void QCALLTYPE AssemblyNative::GetVersion(QCall::AssemblyHandle pAssembly, INT32 UINT16 major=0xffff, minor=0xffff, build=0xffff, revision=0xffff; - pAssembly->GetFile()->GetVersion(&major, &minor, &build, &revision); + pAssembly->GetPEAssembly()->GetVersion(&major, &minor, &build, &revision); *pMajorVersion = major; *pMinorVersion = minor; @@ -478,7 +478,7 @@ void QCALLTYPE AssemblyNative::GetPublicKey(QCall::AssemblyHandle pAssembly, QCa BEGIN_QCALL; DWORD cbPublicKey = 0; - const void *pbPublicKey = pAssembly->GetFile()->GetPublicKey(&cbPublicKey); + const void *pbPublicKey = pAssembly->GetPEAssembly()->GetPublicKey(&cbPublicKey); retPublicKey.SetByteArray((BYTE *)pbPublicKey, cbPublicKey); END_QCALL; @@ -499,7 +499,7 @@ void QCALLTYPE AssemblyNative::GetLocale(QCall::AssemblyHandle pAssembly, QCall: BEGIN_QCALL; - LPCUTF8 pLocale = pAssembly->GetFile()->GetLocale(); + LPCUTF8 pLocale = pAssembly->GetPEAssembly()->GetLocale(); if(pLocale) { retString.Set(pLocale); @@ -519,7 +519,7 @@ BOOL QCALLTYPE AssemblyNative::GetCodeBase(QCall::AssemblyHandle pAssembly, QCal StackSString codebase; { - ret = pAssembly->GetFile()->GetCodeBase(codebase); + ret = pAssembly->GetPEAssembly()->GetCodeBase(codebase); } retString.Set(codebase); @@ -534,7 +534,7 @@ INT32 QCALLTYPE AssemblyNative::GetHashAlgorithm(QCall::AssemblyHandle pAssembly INT32 retVal=0; BEGIN_QCALL; - retVal = pAssembly->GetFile()->GetHashAlgId(); + retVal = pAssembly->GetPEAssembly()->GetHashAlgId(); END_QCALL; return retVal; } @@ -545,7 +545,7 @@ INT32 QCALLTYPE AssemblyNative::GetFlags(QCall::AssemblyHandle pAssembly) INT32 retVal=0; BEGIN_QCALL; - retVal = pAssembly->GetFile()->GetFlags(); + retVal = pAssembly->GetPEAssembly()->GetFlags(); END_QCALL; return retVal; } @@ -639,9 +639,9 @@ void QCALLTYPE AssemblyNative::GetModules(QCall::AssemblyHandle pAssembly, BOOL mdFile mdFile; while (pAssembly->GetMDImport()->EnumNext(&phEnum, &mdFile)) { - DomainFile *pModule = pAssembly->GetModule()->LoadModule(GetAppDomain(), mdFile, fGetResourceModules, !fLoadIfNotFound); - - if (pModule) { + if (fLoadIfNotFound) + { + DomainFile* pModule = pAssembly->GetModule()->LoadModule(GetAppDomain(), mdFile); modules.Append(pModule); } } @@ -1065,7 +1065,7 @@ void QCALLTYPE AssemblyNative::GetFullName(QCall::AssemblyHandle pAssembly, QCal BEGIN_QCALL; StackSString name; - pAssembly->GetFile()->GetDisplayName(name); + pAssembly->GetPEAssembly()->GetDisplayName(name); retString.Set(name); END_QCALL; @@ -1135,12 +1135,12 @@ void QCALLTYPE AssemblyNative::GetImageRuntimeVersion(QCall::AssemblyHandle pAss BEGIN_QCALL; - // Retrieve the PEFile from the assembly. - PEFile* pPEFile = pAssembly->GetFile(); - PREFIX_ASSUME(pPEFile!=NULL); + // Retrieve the PEAssembly from the assembly. + PEAssembly* pPEAssembly = pAssembly->GetPEAssembly(); + PREFIX_ASSUME(pPEAssembly!=NULL); LPCSTR pszVersion = NULL; - IfFailThrow(pPEFile->GetMDImport()->GetVersionString(&pszVersion)); + IfFailThrow(pPEAssembly->GetMDImport()->GetVersionString(&pszVersion)); SString version(SString::Utf8, pszVersion); @@ -1253,7 +1253,7 @@ INT_PTR QCALLTYPE AssemblyNative::GetLoadContextForAssembly(QCall::AssemblyHandl _ASSERTE(pAssembly != NULL); - AssemblyBinder* pAssemblyBinder = pAssembly->GetFile()->GetAssemblyBinder(); + AssemblyBinder* pAssemblyBinder = pAssembly->GetPEAssembly()->GetAssemblyBinder(); if (!pAssemblyBinder->IsDefault()) { @@ -1284,7 +1284,7 @@ BOOL QCALLTYPE AssemblyNative::InternalTryGetRawMetadata( _ASSERTE(lengthRef != nullptr); static_assert_no_msg(sizeof(*lengthRef) == sizeof(COUNT_T)); - metadata = assembly->GetFile()->GetLoadedMetadata(reinterpret_cast(lengthRef)); + metadata = assembly->GetPEAssembly()->GetLoadedMetadata(reinterpret_cast(lengthRef)); *blobRef = reinterpret_cast(const_cast(metadata)); _ASSERTE(*lengthRef >= 0); diff --git a/src/coreclr/vm/assemblynative.hpp b/src/coreclr/vm/assemblynative.hpp index a4ecc62da9a32..6270e5072612e 100644 --- a/src/coreclr/vm/assemblynative.hpp +++ b/src/coreclr/vm/assemblynative.hpp @@ -108,7 +108,7 @@ class AssemblyNative static FCDECL0(uint32_t, GetAssemblyCount); // - // PEFile QCalls + // PEAssembly QCalls // static INT_PTR QCALLTYPE InitializeAssemblyLoadContext(INT_PTR ptrManagedAssemblyLoadContext, BOOL fRepresentsTPALoadContext, BOOL fIsCollectible); diff --git a/src/coreclr/vm/assemblyspec.cpp b/src/coreclr/vm/assemblyspec.cpp index af5862a9ac5ff..dfbee7152df19 100644 --- a/src/coreclr/vm/assemblyspec.cpp +++ b/src/coreclr/vm/assemblyspec.cpp @@ -231,7 +231,7 @@ void AssemblySpec::InitializeSpec(PEAssembly * pFile) INJECT_FAULT(COMPlusThrowOM();); } CONTRACTL_END; - ReleaseHolder pImport(pFile->GetMDImportWithRef()); + IMDInternalImport* pImport = pFile->GetMDImport(); mdAssembly a; IfFailThrow(pImport->GetAssemblyFromScope(&a)); @@ -690,7 +690,7 @@ AssemblyBinder* AssemblySpec::GetBinderFromParentAssembly(AppDomain *pDomain) if(pParentDomainAssembly != NULL) { // Get the PEAssembly associated with the parent's domain assembly - PEAssembly *pParentPEAssembly = pParentDomainAssembly->GetFile(); + PEAssembly *pParentPEAssembly = pParentDomainAssembly->GetPEAssembly(); pParentAssemblyBinder = pParentPEAssembly->GetAssemblyBinder(); } @@ -754,7 +754,7 @@ DomainAssembly *AssemblySpec::LoadDomainAssembly(FileLoadLevel targetLevel, if (pAssembly) { BinderTracing::AssemblyBindOperation bindOperation(this); - bindOperation.SetResult(pAssembly->GetFile(), true /*cached*/); + bindOperation.SetResult(pAssembly->GetPEAssembly(), true /*cached*/); pDomain->LoadDomainFile(pAssembly, targetLevel); RETURN pAssembly; @@ -1219,7 +1219,7 @@ BOOL AssemblySpecBindingCache::StoreAssembly(AssemblySpec *pSpec, DomainAssembly UPTR key = (UPTR)pSpec->Hash(); - AssemblyBinder* pBinderContextForLookup = pAssembly->GetFile()->GetAssemblyBinder(); + AssemblyBinder* pBinderContextForLookup = pAssembly->GetPEAssembly()->GetAssemblyBinder(); key = key ^ (UPTR)pBinderContextForLookup; if (!pSpec->GetBinder()) @@ -1240,13 +1240,13 @@ BOOL AssemblySpecBindingCache::StoreAssembly(AssemblySpec *pSpec, DomainAssembly } entry = abHolder.CreateAssemblyBinding(pHeap); - entry->Init(pSpec,pAssembly->GetFile(),pAssembly,NULL,pHeap, abHolder.GetPamTracker()); + entry->Init(pSpec,pAssembly->GetPEAssembly(),pAssembly,NULL,pHeap, abHolder.GetPamTracker()); m_map.InsertValue(key, entry); abHolder.SuppressRelease(); - STRESS_LOG2(LF_CLASSLOADER,LL_INFO10,"StoreFile (StoreAssembly): Add cached entry (%p) with PEFile %p",entry,pAssembly->GetFile()); + STRESS_LOG2(LF_CLASSLOADER,LL_INFO10,"StorePEAssembly (StoreAssembly): Add cached entry (%p) with PEAssembly %p",entry,pAssembly->GetPEAssembly()); RETURN TRUE; } else @@ -1263,7 +1263,7 @@ BOOL AssemblySpecBindingCache::StoreAssembly(AssemblySpec *pSpec, DomainAssembly { // OK if we have have a matching PEAssembly if (entry->GetFile() != NULL - && pAssembly->GetFile()->Equals(entry->GetFile())) + && pAssembly->GetPEAssembly()->Equals(entry->GetFile())) { entry->SetAssembly(pAssembly); RETURN TRUE; @@ -1280,7 +1280,7 @@ BOOL AssemblySpecBindingCache::StoreAssembly(AssemblySpec *pSpec, DomainAssembly // Returns TRUE if add was successful - if FALSE is returned, caller should honor current // cached value to ensure consistency. -BOOL AssemblySpecBindingCache::StoreFile(AssemblySpec *pSpec, PEAssembly *pFile) +BOOL AssemblySpecBindingCache::StorePEAssembly(AssemblySpec *pSpec, PEAssembly *pPEAssembly) { CONTRACT(BOOL) { @@ -1288,14 +1288,14 @@ BOOL AssemblySpecBindingCache::StoreFile(AssemblySpec *pSpec, PEAssembly *pFile) THROWS; GC_TRIGGERS; MODE_ANY; - POSTCONDITION((!RETVAL) || (UnsafeContains(this, pSpec) && UnsafeVerifyLookupFile(this, pSpec, pFile))); + POSTCONDITION((!RETVAL) || (UnsafeContains(this, pSpec) && UnsafeVerifyLookupFile(this, pSpec, pPEAssembly))); INJECT_FAULT(COMPlusThrowOM();); } CONTRACT_END; UPTR key = (UPTR)pSpec->Hash(); - AssemblyBinder* pBinderContextForLookup = pFile->GetAssemblyBinder(); + AssemblyBinder* pBinderContextForLookup = pPEAssembly->GetAssemblyBinder(); key = key ^ (UPTR)pBinderContextForLookup; if (!pSpec->GetBinder()) @@ -1325,12 +1325,12 @@ BOOL AssemblySpecBindingCache::StoreFile(AssemblySpec *pSpec, PEAssembly *pFile) entry = abHolder.CreateAssemblyBinding(pHeap); - entry->Init(pSpec,pFile,NULL,NULL,pHeap, abHolder.GetPamTracker()); + entry->Init(pSpec, pPEAssembly,NULL,NULL,pHeap, abHolder.GetPamTracker()); m_map.InsertValue(key, entry); abHolder.SuppressRelease(); - STRESS_LOG2(LF_CLASSLOADER,LL_INFO10,"StoreFile: Add cached entry (%p) with PEFile %p\n", entry, pFile); + STRESS_LOG2(LF_CLASSLOADER,LL_INFO10,"StorePEAssembly: Add cached entry (%p) with PEAssembly %p\n", entry, pPEAssembly); RETURN TRUE; } @@ -1340,7 +1340,7 @@ BOOL AssemblySpecBindingCache::StoreFile(AssemblySpec *pSpec, PEAssembly *pFile) { // OK if this is a duplicate if (entry->GetFile() != NULL - && pFile->Equals(entry->GetFile())) + && pPEAssembly->Equals(entry->GetFile())) RETURN TRUE; } else @@ -1350,7 +1350,7 @@ BOOL AssemblySpecBindingCache::StoreFile(AssemblySpec *pSpec, PEAssembly *pFile) entry->ThrowIfError(); } - STRESS_LOG2(LF_CLASSLOADER,LL_INFO10,"Incompatible cached entry found (%p) when adding PEFile %p\n", entry, pFile); + STRESS_LOG2(LF_CLASSLOADER,LL_INFO10,"Incompatible cached entry found (%p) when adding PEAssembly %p\n", entry, pPEAssembly); // Invalid cache transition (see above note about state transitions) RETURN FALSE; } @@ -1396,7 +1396,7 @@ BOOL AssemblySpecBindingCache::StoreException(AssemblySpec *pSpec, Exception* pE m_map.InsertValue(key, entry); abHolder.SuppressRelease(); - STRESS_LOG2(LF_CLASSLOADER,LL_INFO10,"StoreFile (StoreException): Add cached entry (%p) with exception %p",entry,pEx); + STRESS_LOG2(LF_CLASSLOADER,LL_INFO10,"StorePEAssembly (StoreException): Add cached entry (%p) with exception %p",entry,pEx); RETURN TRUE; } else diff --git a/src/coreclr/vm/assemblyspec.hpp b/src/coreclr/vm/assemblyspec.hpp index 63ffac82f6e7a..5d09b5b75a664 100644 --- a/src/coreclr/vm/assemblyspec.hpp +++ b/src/coreclr/vm/assemblyspec.hpp @@ -102,7 +102,7 @@ class AssemblySpec : public BaseAssemblySpec }; - void InitializeSpec(PEAssembly *pFile); + void InitializeSpec(PEAssembly* pPEAssembly); HRESULT InitializeSpec(StackingAllocator* alloc, ASSEMBLYNAMEREF* pName, BOOL fParse = TRUE); @@ -332,8 +332,8 @@ class AssemblySpecBindingCache { WRAPPER_NO_CONTRACT; - if (m_pFile != NULL) - m_pFile->Release(); + if (m_pPEAssembly != NULL) + m_pPEAssembly->Release(); if (m_exceptionType==EXTYPE_EE) delete m_pException; @@ -341,7 +341,7 @@ class AssemblySpecBindingCache inline DomainAssembly* GetAssembly(){ LIMITED_METHOD_CONTRACT; return m_pAssembly;}; inline void SetAssembly(DomainAssembly* pAssembly){ LIMITED_METHOD_CONTRACT; m_pAssembly=pAssembly;}; - inline PEAssembly* GetFile(){ LIMITED_METHOD_CONTRACT; return m_pFile;}; + inline PEAssembly* GetFile(){ LIMITED_METHOD_CONTRACT; return m_pPEAssembly;}; inline BOOL IsError(){ LIMITED_METHOD_CONTRACT; return (m_exceptionType!=EXTYPE_NONE);}; // bound to the file, but failed later @@ -365,7 +365,7 @@ class AssemblySpecBindingCache default: _ASSERTE(!"Unexpected exception type"); } }; - inline void Init(AssemblySpec* pSpec, PEAssembly* pFile, DomainAssembly* pAssembly, Exception* pEx, LoaderHeap *pHeap, AllocMemTracker *pamTracker) + inline void Init(AssemblySpec* pSpec, PEAssembly* pPEAssembly, DomainAssembly* pAssembly, Exception* pEx, LoaderHeap *pHeap, AllocMemTracker *pamTracker) { CONTRACTL { @@ -375,7 +375,7 @@ class AssemblySpecBindingCache } CONTRACTL_END; - InitInternal(pSpec,pFile,pAssembly); + InitInternal(pSpec,pPEAssembly,pAssembly); if (pHeap != NULL) { m_spec.CloneFieldsToLoaderHeap(AssemblySpec::ALL_OWNED,pHeap, pamTracker); @@ -444,19 +444,19 @@ class AssemblySpecBindingCache }; protected: - inline void InitInternal(AssemblySpec* pSpec, PEAssembly* pFile, DomainAssembly* pAssembly ) + inline void InitInternal(AssemblySpec* pSpec, PEAssembly* pPEAssembly, DomainAssembly* pAssembly ) { WRAPPER_NO_CONTRACT; m_spec.CopyFrom(pSpec); - m_pFile = pFile; - if (m_pFile) - m_pFile->AddRef(); + m_pPEAssembly = pPEAssembly; + if (m_pPEAssembly) + m_pPEAssembly->AddRef(); m_pAssembly = pAssembly; m_exceptionType=EXTYPE_NONE; } AssemblySpec m_spec; - PEAssembly *m_pFile; + PEAssembly *m_pPEAssembly; DomainAssembly *m_pAssembly; enum{ EXTYPE_NONE = 0x00000000, @@ -490,7 +490,7 @@ class AssemblySpecBindingCache PEAssembly *LookupFile(AssemblySpec *pSpec, BOOL fThrow = TRUE); BOOL StoreAssembly(AssemblySpec *pSpec, DomainAssembly *pAssembly); - BOOL StoreFile(AssemblySpec *pSpec, PEAssembly *pFile); + BOOL StorePEAssembly(AssemblySpec *pSpec, PEAssembly *pPEAssembly); BOOL StoreException(AssemblySpec *pSpec, Exception* pEx); diff --git a/src/coreclr/vm/ceeload.cpp b/src/coreclr/vm/ceeload.cpp index 3ffb9a83b1511..02374e18df61c 100644 --- a/src/coreclr/vm/ceeload.cpp +++ b/src/coreclr/vm/ceeload.cpp @@ -214,7 +214,7 @@ void Module::UpdateNewlyAddedTypes() // R2R pre-computes an export table and tries to avoid populating a class hash at runtime. However the profiler can // still add new types on the fly by calling here. If that occurs we fallback to the slower path of creating the // in memory hashtable as usual. - if (!IsResource() && GetAvailableClassHash() == NULL) + if (GetAvailableClassHash() == NULL) { // This call will populate the hash tables with anything that is in metadata already. GetClassLoader()->LazyPopulateCaseSensitiveHashTablesDontHaveLock(); @@ -266,12 +266,9 @@ void Module::NotifyProfilerLoadFinished(HRESULT hr) if (SetTransientFlagInterlocked(IS_PROFILER_NOTIFIED)) { // Record how many types are already present - if (!IsResource()) - { - m_dwTypeCount = GetMDImport()->GetCountWithTokenKind(mdtTypeDef); - m_dwExportedTypeCount = GetMDImport()->GetCountWithTokenKind(mdtExportedType); - m_dwCustomAttributeCount = GetMDImport()->GetCountWithTokenKind(mdtCustomAttribute); - } + m_dwTypeCount = GetMDImport()->GetCountWithTokenKind(mdtTypeDef); + m_dwExportedTypeCount = GetMDImport()->GetCountWithTokenKind(mdtExportedType); + m_dwCustomAttributeCount = GetMDImport()->GetCountWithTokenKind(mdtCustomAttribute); BOOL profilerCallbackHappened = FALSE; // Notify the profiler, this may cause metadata to be updated @@ -294,7 +291,7 @@ void Module::NotifyProfilerLoadFinished(HRESULT hr) // If there are more types than before, add these new types to the // assembly - if (profilerCallbackHappened && !IsResource()) + if (profilerCallbackHappened) { UpdateNewlyAddedTypes(); } @@ -337,7 +334,7 @@ void Module::NotifyEtwLoadFinished(HRESULT hr) // The constructor phase initializes just enough so that Destruct() can be safely called. // It cannot throw or fail. // -Module::Module(Assembly *pAssembly, mdFile moduleRef, PEFile *file) +Module::Module(Assembly *pAssembly, mdFile moduleRef, PEAssembly *pPEAssembly) { CONTRACTL { @@ -351,13 +348,13 @@ Module::Module(Assembly *pAssembly, mdFile moduleRef, PEFile *file) m_pAssembly = pAssembly; m_moduleRef = moduleRef; - m_file = file; + m_pPEAssembly = pPEAssembly; m_dwTransientFlags = CLASSES_FREED; // Memory allocated on LoaderHeap is zero-filled. Spot-check it here. _ASSERTE(m_pBinder == NULL); - file->AddRef(); + pPEAssembly->AddRef(); } void Module::InitializeForProfiling() @@ -457,7 +454,7 @@ void Module::Initialize(AllocMemTracker *pamTracker, LPCWSTR szName) } CONTRACTL_END; - m_pSimpleName = m_file->GetSimpleName(); + m_pSimpleName = m_pPEAssembly->GetSimpleName(); m_Crst.Init(CrstModule); m_LookupTableCrst.Init(CrstModuleLookupTable, CrstFlags(CRST_UNSAFE_ANYMODE | CRST_DEBUGGER_THREAD)); @@ -486,62 +483,56 @@ void Module::Initialize(AllocMemTracker *pamTracker, LPCWSTR szName) #ifdef FEATURE_READYTORUN m_pNativeImage = NULL; - if (!IsResource()) + if ((m_pReadyToRunInfo = ReadyToRunInfo::Initialize(this, pamTracker)) != NULL) { - if ((m_pReadyToRunInfo = ReadyToRunInfo::Initialize(this, pamTracker)) != NULL) + m_pNativeImage = m_pReadyToRunInfo->GetNativeImage(); + if (m_pNativeImage != NULL) { - m_pNativeImage = m_pReadyToRunInfo->GetNativeImage(); - if (m_pNativeImage != NULL) - { - m_NativeMetadataAssemblyRefMap = m_pNativeImage->GetManifestMetadataAssemblyRefMap(); - } - else + m_NativeMetadataAssemblyRefMap = m_pNativeImage->GetManifestMetadataAssemblyRefMap(); + } + else + { + // For composite images, manifest metadata gets loaded as part of the native image + COUNT_T cMeta = 0; + if (GetPEAssembly()->GetPEImage()->GetNativeManifestMetadata(&cMeta) != NULL) { - // For composite images, manifest metadata gets loaded as part of the native image - COUNT_T cMeta = 0; - if (GetFile()->GetOpenedILimage()->GetNativeManifestMetadata(&cMeta) != NULL) - { - // Load the native assembly import - GetNativeAssemblyImport(TRUE /* loadAllowed */); - } + // Load the native assembly import + GetNativeAssemblyImport(TRUE /* loadAllowed */); } } } #endif - // Initialize the instance fields that we need for all non-Resource Modules - if (!IsResource()) + // Initialize the instance fields that we need for all Modules + if (m_pAvailableClasses == NULL && !IsReadyToRun()) { - if (m_pAvailableClasses == NULL && !IsReadyToRun()) - { - m_pAvailableClasses = EEClassHashTable::Create(this, - GetAssembly()->IsCollectible() ? AVAILABLE_CLASSES_HASH_BUCKETS_COLLECTIBLE : AVAILABLE_CLASSES_HASH_BUCKETS, - FALSE /* bCaseInsensitive */, pamTracker); - } + m_pAvailableClasses = EEClassHashTable::Create(this, + GetAssembly()->IsCollectible() ? AVAILABLE_CLASSES_HASH_BUCKETS_COLLECTIBLE : AVAILABLE_CLASSES_HASH_BUCKETS, + FALSE /* bCaseInsensitive */, pamTracker); + } - if (m_pAvailableParamTypes == NULL) - { - m_pAvailableParamTypes = EETypeHashTable::Create(GetLoaderAllocator(), this, PARAMTYPES_HASH_BUCKETS, pamTracker); - } + if (m_pAvailableParamTypes == NULL) + { + m_pAvailableParamTypes = EETypeHashTable::Create(GetLoaderAllocator(), this, PARAMTYPES_HASH_BUCKETS, pamTracker); + } - if (m_pInstMethodHashTable == NULL) + if (m_pInstMethodHashTable == NULL) + { + m_pInstMethodHashTable = InstMethodHashTable::Create(GetLoaderAllocator(), this, PARAMMETHODS_HASH_BUCKETS, pamTracker); + } + + if (m_pMemberRefToDescHashTable == NULL) + { + if (IsReflection()) { - m_pInstMethodHashTable = InstMethodHashTable::Create(GetLoaderAllocator(), this, PARAMMETHODS_HASH_BUCKETS, pamTracker); + m_pMemberRefToDescHashTable = MemberRefToDescHashTable::Create(this, MEMBERREF_MAP_INITIAL_SIZE, pamTracker); } - - if(m_pMemberRefToDescHashTable == NULL) + else { - if (IsReflection()) - { - m_pMemberRefToDescHashTable = MemberRefToDescHashTable::Create(this, MEMBERREF_MAP_INITIAL_SIZE, pamTracker); - } - else - { - IMDInternalImport * pImport = GetMDImport(); + IMDInternalImport* pImport = GetMDImport(); - // Get #MemberRefs and create memberrefToDesc hash table - m_pMemberRefToDescHashTable = MemberRefToDescHashTable::Create(this, pImport->GetCountWithTokenKind(mdtMemberRef)+1, pamTracker); - } + // Get #MemberRefs and create memberrefToDesc hash table + m_pMemberRefToDescHashTable = MemberRefToDescHashTable::Create(this, pImport->GetCountWithTokenKind(mdtMemberRef) + 1, pamTracker); } } @@ -563,7 +554,7 @@ void Module::Initialize(AllocMemTracker *pamTracker, LPCWSTR szName) InitializeForProfiling(); } - if (!IsResource() && (m_AssemblyRefByNameTable == NULL)) + if (m_AssemblyRefByNameTable == NULL) { Module::CreateAssemblyRefByNameTable(pamTracker); } @@ -744,16 +735,15 @@ void Module::SetDebuggerInfoBits(DebuggerAssemblyControlFlags newBits) #ifndef DACCESS_COMPILE /* static */ -Module *Module::Create(Assembly *pAssembly, mdFile moduleRef, PEFile *file, AllocMemTracker *pamTracker) +Module *Module::Create(Assembly *pAssembly, mdFile moduleRef, PEAssembly *pPEAssembly, AllocMemTracker *pamTracker) { CONTRACT(Module *) { STANDARD_VM_CHECK; PRECONDITION(CheckPointer(pAssembly)); - PRECONDITION(CheckPointer(file)); - PRECONDITION(!IsNilToken(moduleRef) || file->IsAssembly()); + PRECONDITION(CheckPointer(pPEAssembly)); POSTCONDITION(CheckPointer(RETVAL)); - POSTCONDITION(RETVAL->GetFile() == file); + POSTCONDITION(RETVAL->GetPEAssembly() == pPEAssembly); } CONTRACT_END; @@ -764,19 +754,19 @@ Module *Module::Create(Assembly *pAssembly, mdFile moduleRef, PEFile *file, Allo // Create the module #ifdef EnC_SUPPORTED - if (IsEditAndContinueCapable(pAssembly, file)) + if (IsEditAndContinueCapable(pAssembly, pPEAssembly)) { // if file is EnCCapable, always create an EnC-module, but EnC won't necessarily be enabled. // Debugger enables this by calling SetJITCompilerFlags on LoadModule callback. void* pMemory = pamTracker->Track(pAssembly->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(EditAndContinueModule)))); - pModule = new (pMemory) EditAndContinueModule(pAssembly, moduleRef, file); + pModule = new (pMemory) EditAndContinueModule(pAssembly, moduleRef, pPEAssembly); } else #endif // EnC_SUPPORTED { void* pMemory = pamTracker->Track(pAssembly->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(Module)))); - pModule = new (pMemory) Module(pAssembly, moduleRef, file); + pModule = new (pMemory) Module(pAssembly, moduleRef, pPEAssembly); } PREFIX_ASSUME(pModule != NULL); @@ -802,10 +792,7 @@ void Module::ApplyMetaData() ULONG ulCount; #if defined(PROFILING_SUPPORTED) || defined(EnC_SUPPORTED) - if (!IsResource()) - { - UpdateNewlyAddedTypes(); - } + UpdateNewlyAddedTypes(); #endif // PROFILING_SUPPORTED || EnC_SUPPORTED // Ensure for TypeRef @@ -953,7 +940,7 @@ void Module::Destruct() delete m_debuggerSpecificData.m_pILOffsetMappingTable; } - m_file->Release(); + m_pPEAssembly->Release(); // If this module was loaded as domain-specific, then // we must free its ModuleIndex so that it can be reused @@ -972,7 +959,7 @@ bool Module::NeedsGlobalMethodTable() CONTRACTL_END; IMDInternalImport * pImport = GetMDImport(); - if (!IsResource() && pImport->IsValidToken(COR_GLOBAL_PARENT_TOKEN)) + if (pImport->IsValidToken(COR_GLOBAL_PARENT_TOKEN)) { { HENUMInternalHolder funcEnum(pImport); @@ -1032,7 +1019,7 @@ MethodTable *Module::GetGlobalMethodTable() #endif // !DACCESS_COMPILE /*static*/ -BOOL Module::IsEditAndContinueCapable(Assembly *pAssembly, PEFile *file) +BOOL Module::IsEditAndContinueCapable(Assembly *pAssembly, PEAssembly *pPEAssembly) { CONTRACTL { @@ -1043,13 +1030,12 @@ BOOL Module::IsEditAndContinueCapable(Assembly *pAssembly, PEFile *file) } CONTRACTL_END; - _ASSERTE(pAssembly != NULL && file != NULL); + _ASSERTE(pAssembly != NULL && pPEAssembly != NULL); // Some modules are never EnC-capable return ! (pAssembly->GetDebuggerInfoBits() & DACF_ALLOW_JIT_OPTS || - file->IsSystem() || - file->IsResource() || - file->IsDynamic()); + pPEAssembly->IsSystem() || + pPEAssembly->IsDynamic()); } BOOL Module::IsManifest() @@ -1975,17 +1961,6 @@ void Module::AllocateStatics(AllocMemTracker *pamTracker) { STANDARD_VM_CONTRACT; - if (IsResource()) - { - m_dwRegularStaticsBlockSize = DomainLocalModule::OffsetOfDataBlob(); - m_dwThreadStaticsBlockSize = ThreadLocalModule::OffsetOfDataBlob(); - - // If it has no code, we don't have to allocate anything - LOG((LF_CLASSLOADER, LL_INFO10000, "STATICS: Resource module %s. No statics needed\n", GetSimpleName())); - _ASSERTE(m_maxTypeRidStaticsAllocated == 0); - return; - } - LOG((LF_CLASSLOADER, LL_INFO10000, "STATICS: Allocating statics for module %s\n", GetSimpleName())); // Build the offset table, which will tell us what the offsets for the statics of each class are (one offset for gc handles, one offset @@ -2114,9 +2089,6 @@ void Module::AllocateMaps() PTR_TADDR pTable = NULL; - if (IsResource()) - return; - if (IsReflection()) { // For dynamic modules, it is essential that we at least have a TypeDefToMethodTable @@ -2402,7 +2374,7 @@ BOOL Module::IsInSameVersionBubble(Module *target) { // Check if the current module's image has native manifest metadata, otherwise the current->GetNativeAssemblyImport() asserts. COUNT_T cMeta=0; - const void* pMeta = GetFile()->GetOpenedILimage()->GetNativeManifestMetadata(&cMeta); + const void* pMeta = GetPEAssembly()->GetPEImage()->GetNativeManifestMetadata(&cMeta); if (pMeta == NULL) { return FALSE; @@ -2529,10 +2501,6 @@ ISymUnmanagedReader *Module::GetISymUnmanagedReader(void) } CONTRACT_END; - // No symbols for resource modules - if (IsResource()) - RETURN NULL; - if (g_fEEShutDown) RETURN NULL; @@ -2565,16 +2533,15 @@ ISymUnmanagedReader *Module::GetISymUnmanagedReader(void) // There are 4 main cases here: // 1. Assembly is on disk and we'll get the symbols from a file next to the assembly - // 2. Assembly is provided by the host and we'll get the symbols from the host - // 3. Assembly was loaded in-memory (by byte array or ref-emit), and symbols were + // 2. Assembly was loaded in-memory (by byte array or ref-emit), and symbols were // provided along with it. - // 4. Assembly was loaded in-memory but no symbols were provided. + // 3. Assembly was loaded in-memory but no symbols were provided. - // Determine whether we should be looking in memory for the symbols (cases 2 & 3) - bool fInMemorySymbols = ( m_file->IsIStream() || GetInMemorySymbolStream() ); - if( !fInMemorySymbols && m_file->GetPath().IsEmpty() ) + // Determine whether we should be looking in memory for the symbols (case 2) + bool fInMemorySymbols = GetInMemorySymbolStream(); + if( !fInMemorySymbols && m_pPEAssembly->GetPath().IsEmpty() ) { - // Case 4. We don't have a module path, an IStream or an in memory symbol stream, + // Case 3. We don't have a module path or an in memory symbol stream, // so there is no-where to try and get symbols from. RETURN (NULL); } @@ -2657,7 +2624,7 @@ ISymUnmanagedReader *Module::GetISymUnmanagedReader(void) else { // The assembly is on disk, so try and load symbols based on the path to the assembly (case 1) - const SString &path = m_file->GetPath(); + const SString &path = m_pPEAssembly->GetPath(); // Call Fusion to ensure that any PDB's are shadow copied before // trying to get a symbol reader. This has to be done once per @@ -2856,7 +2823,6 @@ void Module::AddClass(mdTypeDef classdef) THROWS; GC_TRIGGERS; MODE_PREEMPTIVE; - PRECONDITION(!IsResource()); } CONTRACTL_END; @@ -2913,8 +2879,8 @@ void Module::BuildClassForModule() // Returns true iff the debugger should be notified about this module // // Notes: -// Debugger doesn't need to be notified about modules that can't be executed, -// like inspection and resource only. These are just pure data. +// Debugger doesn't need to be notified about modules that can't be executed. +// (we do not have such cases at the moment) // // This should be immutable for an instance of a module. That ensures that the debugger gets consistent // notifications about it. It this value mutates, than the debugger may miss relevant notifications. @@ -2923,11 +2889,6 @@ BOOL Module::IsVisibleToDebugger() WRAPPER_NO_CONTRACT; SUPPORTS_DAC; - if (IsResource()) - { - return FALSE; - } - return TRUE; } @@ -2987,7 +2948,7 @@ TADDR Module::GetIL(DWORD target) if (target == 0) return NULL; - return m_file->GetIL(target); + return m_pPEAssembly->GetIL(target); } PTR_VOID Module::GetRvaField(DWORD rva) @@ -2995,7 +2956,7 @@ PTR_VOID Module::GetRvaField(DWORD rva) WRAPPER_NO_CONTRACT; SUPPORTS_DAC; - return m_file->GetRvaField(rva); + return m_pPEAssembly->GetRvaField(rva); } #ifndef DACCESS_COMPILE @@ -3004,7 +2965,7 @@ CHECK Module::CheckRvaField(RVA field) { WRAPPER_NO_CONTRACT; if (!IsReflection()) - CHECK(m_file->CheckRvaField(field)); + CHECK(m_pPEAssembly->CheckRvaField(field)); CHECK_OK; } @@ -3018,7 +2979,7 @@ CHECK Module::CheckRvaField(RVA field, COUNT_T size) CONTRACTL_END; if (!IsReflection()) - CHECK(m_file->CheckRvaField(field, size)); + CHECK(m_pPEAssembly->CheckRvaField(field, size)); CHECK_OK; } @@ -3028,28 +2989,28 @@ BOOL Module::HasTls() { WRAPPER_NO_CONTRACT; - return m_file->HasTls(); + return m_pPEAssembly->HasTls(); } BOOL Module::IsRvaFieldTls(DWORD rva) { WRAPPER_NO_CONTRACT; - return m_file->IsRvaFieldTls(rva); + return m_pPEAssembly->IsRvaFieldTls(rva); } UINT32 Module::GetFieldTlsOffset(DWORD rva) { WRAPPER_NO_CONTRACT; - return m_file->GetFieldTlsOffset(rva); + return m_pPEAssembly->GetFieldTlsOffset(rva); } UINT32 Module::GetTlsIndex() { WRAPPER_NO_CONTRACT; - return m_file->GetTlsIndex(); + return m_pPEAssembly->GetTlsIndex(); } @@ -3077,7 +3038,7 @@ BOOL Module::IsSigInIL(PCCOR_SIGNATURE signature) } CONTRACTL_END; - return m_file->IsPtrInILImage(signature); + return m_pPEAssembly->IsPtrInPEImage(signature); } void Module::InitializeStringData(DWORD token, EEStringData *pstrData, CQuickBytes *pqb) @@ -3182,7 +3143,7 @@ mdToken Module::GetEntryPointToken() { WRAPPER_NO_CONTRACT; - return m_file->GetEntryPointToken(); + return m_pPEAssembly->GetEntryPointToken(); } BYTE *Module::GetProfilerBase() @@ -3195,13 +3156,13 @@ BYTE *Module::GetProfilerBase() } CONTRACT_END; - if (m_file == NULL) // I'd rather assert this is not the case... + if (m_pPEAssembly == NULL) // I'd rather assert this is not the case... { RETURN NULL; } - else if (m_file->IsLoaded()) + else if (m_pPEAssembly->HasLoadedPEImage()) { - RETURN (BYTE*)(m_file->GetLoadedIL()->GetBase()); + RETURN (BYTE*)(m_pPEAssembly->GetLoadedLayout()->GetBase()); } else { @@ -3413,20 +3374,18 @@ DomainAssembly * Module::LoadAssembly(mdAssemblyRef kAssemblyRef) } { - PEAssemblyHolder pFile = GetDomainAssembly()->GetFile()->LoadAssembly( - kAssemblyRef, - NULL); + PEAssemblyHolder pPEAssembly = GetDomainAssembly()->GetPEAssembly()->LoadAssembly(kAssemblyRef); AssemblySpec spec; spec.InitializeSpec(kAssemblyRef, GetMDImport(), GetDomainAssembly()); // Set the binding context in the AssemblySpec if one is available. This can happen if the LoadAssembly ended up // invoking the custom AssemblyLoadContext implementation that returned a reference to an assembly bound to a different // AssemblyLoadContext implementation. - AssemblyBinder *pBinder = pFile->GetAssemblyBinder(); + AssemblyBinder *pBinder = pPEAssembly->GetAssemblyBinder(); if (pBinder != NULL) { spec.SetBinder(pBinder); } - pDomainAssembly = GetAppDomain()->LoadDomainAssembly(&spec, pFile, FILE_LOADED); + pDomainAssembly = GetAppDomain()->LoadDomainAssembly(&spec, pPEAssembly, FILE_LOADED); } if (pDomainAssembly != NULL) @@ -3434,7 +3393,7 @@ DomainAssembly * Module::LoadAssembly(mdAssemblyRef kAssemblyRef) _ASSERTE( pDomainAssembly->IsSystem() || // GetAssemblyIfLoaded will not find CoreLib (see AppDomain::FindCachedFile) !pDomainAssembly->IsLoaded() || // GetAssemblyIfLoaded will not find not-yet-loaded assemblies - GetAssemblyIfLoaded(kAssemblyRef, NULL, FALSE, pDomainAssembly->GetFile()->GetHostAssembly()->GetBinder()) != NULL); // GetAssemblyIfLoaded should find all remaining cases + GetAssemblyIfLoaded(kAssemblyRef, NULL, FALSE, pDomainAssembly->GetPEAssembly()->GetHostAssembly()->GetBinder()) != NULL); // GetAssemblyIfLoaded should find all remaining cases if (pDomainAssembly->GetCurrentAssembly() != NULL) { @@ -3447,7 +3406,7 @@ DomainAssembly * Module::LoadAssembly(mdAssemblyRef kAssemblyRef) #endif // !DACCESS_COMPILE -Module *Module::GetModuleIfLoaded(mdFile kFile, BOOL onlyLoadedInAppDomain, BOOL permitResources) +Module *Module::GetModuleIfLoaded(mdFile kFile) { CONTRACT(Module *) { @@ -3479,7 +3438,7 @@ Module *Module::GetModuleIfLoaded(mdFile kFile, BOOL onlyLoadedInAppDomain, BOOL if (kFile == mdTokenNil) RETURN NULL; - RETURN GetAssembly()->GetManifestModule()->GetModuleIfLoaded(kFile, onlyLoadedInAppDomain, permitResources); + RETURN GetAssembly()->GetManifestModule()->GetModuleIfLoaded(kFile); } Module *pModule = LookupFile(kFile); @@ -3519,10 +3478,6 @@ Module *Module::GetModuleIfLoaded(mdFile kFile, BOOL onlyLoadedInAppDomain, BOOL #endif } - // We may not want to return a resource module - if (!permitResources && pModule && pModule->IsResource()) - pModule = NULL; - #ifndef DACCESS_COMPILE #endif // !DACCESS_COMPILE RETURN pModule; @@ -3530,8 +3485,7 @@ Module *Module::GetModuleIfLoaded(mdFile kFile, BOOL onlyLoadedInAppDomain, BOOL #ifndef DACCESS_COMPILE -DomainFile *Module::LoadModule(AppDomain *pDomain, mdFile kFile, - BOOL permitResources/*=TRUE*/, BOOL bindOnly/*=FALSE*/) +DomainFile *Module::LoadModule(AppDomain *pDomain, mdFile kFile) { CONTRACT(DomainFile *) { @@ -3541,38 +3495,32 @@ DomainFile *Module::LoadModule(AppDomain *pDomain, mdFile kFile, MODE_ANY; PRECONDITION(TypeFromToken(kFile) == mdtFile || TypeFromToken(kFile) == mdtModuleRef); - POSTCONDITION(CheckPointer(RETVAL, !permitResources || bindOnly ? NULL_OK : NULL_NOT_OK)); } CONTRACT_END; - if (bindOnly) + LPCSTR psModuleName=NULL; + if (TypeFromToken(kFile) == mdtModuleRef) { - RETURN NULL; + // This is a moduleRef + IfFailThrow(GetMDImport()->GetModuleRefProps(kFile, &psModuleName)); } else { - LPCSTR psModuleName=NULL; - if (TypeFromToken(kFile) == mdtModuleRef) - { - // This is a moduleRef - IfFailThrow(GetMDImport()->GetModuleRefProps(kFile, &psModuleName)); - } - else - { - // This is mdtFile - IfFailThrow(GetAssembly()->GetManifestImport()->GetFileProps(kFile, - &psModuleName, - NULL, - NULL, - NULL)); - } - SString name(SString::Utf8, psModuleName); - EEFileLoadException::Throw(name, COR_E_MULTIMODULEASSEMBLIESDIALLOWED, NULL); + // This is mdtFile + IfFailThrow(GetAssembly()->GetManifestImport()->GetFileProps(kFile, + &psModuleName, + NULL, + NULL, + NULL)); } + + SString name(SString::Utf8, psModuleName); + EEFileLoadException::Throw(name, COR_E_MULTIMODULEASSEMBLIESDIALLOWED, NULL); + RETURN NULL; } #endif // !DACCESS_COMPILE -PTR_Module Module::LookupModule(mdToken kFile,BOOL permitResources/*=TRUE*/) +PTR_Module Module::LookupModule(mdToken kFile) { CONTRACT(PTR_Module) { @@ -3598,7 +3546,7 @@ PTR_Module Module::LookupModule(mdToken kFile,BOOL permitResources/*=TRUE*/) if (kFileLocal == mdTokenNil) COMPlusThrowHR(COR_E_BADIMAGEFORMAT); - RETURN GetAssembly()->GetManifestModule()->LookupModule(kFileLocal, permitResources); + RETURN GetAssembly()->GetManifestModule()->LookupModule(kFileLocal); } PTR_Module pModule = LookupFile(kFile); @@ -4111,8 +4059,8 @@ BOOL Module::NotifyDebuggerLoad(AppDomain *pDomain, DomainFile * pDomainFile, in if (flags & ATTACH_MODULE_LOAD) { g_pDebugInterface->LoadModule(this, - m_file->GetPath(), - m_file->GetPath().GetCount(), + m_pPEAssembly->GetPath(), + m_pPEAssembly->GetPath().GetCount(), GetAssembly(), pDomain, pDomainFile, @@ -4168,9 +4116,9 @@ using GetTokenForVTableEntry_t = mdToken(STDMETHODCALLTYPE*)(HMODULE module, BYT static HMODULE GetIJWHostForModule(Module* module) { #if !defined(TARGET_UNIX) - PEDecoder* pe = module->GetFile()->GetLoadedIL(); + PEDecoder* pe = module->GetPEAssembly()->GetLoadedLayout(); - BYTE* baseAddress = (BYTE*)module->GetFile()->GetIJWBase(); + BYTE* baseAddress = (BYTE*)module->GetPEAssembly()->GetIJWBase(); IMAGE_IMPORT_DESCRIPTOR* importDescriptor = (IMAGE_IMPORT_DESCRIPTOR*)pe->GetDirectoryData(pe->GetDirectoryEntry(IMAGE_DIRECTORY_ENTRY_IMPORT)); @@ -4286,7 +4234,7 @@ void Module::FixupVTables() // If we've already fixed up, or this is not an IJW module, just return. // NOTE: This relies on ILOnly files not having fixups. If this changes, // we need to change this conditional. - if (IsIJWFixedUp() || m_file->IsILOnly()) { + if (IsIJWFixedUp() || m_pPEAssembly->IsILOnly()) { return; } @@ -4302,11 +4250,11 @@ void Module::FixupVTables() GetTokenForVTableEntryCallback = GetTokenForVTableEntry; } - HINSTANCE hInstThis = GetFile()->GetIJWBase(); + HINSTANCE hInstThis = GetPEAssembly()->GetIJWBase(); // Get vtable fixup data COUNT_T cFixupRecords; - IMAGE_COR_VTABLEFIXUP *pFixupTable = m_file->GetVTableFixups(&cFixupRecords); + IMAGE_COR_VTABLEFIXUP *pFixupTable = m_pPEAssembly->GetVTableFixups(&cFixupRecords); // No records then return if (cFixupRecords == 0) { @@ -4314,7 +4262,7 @@ void Module::FixupVTables() } // Now, we need to take a lock to serialize fixup. - PEImage::IJWFixupData *pData = PEImage::GetIJWData(m_file->GetIJWBase()); + PEImage::IJWFixupData *pData = PEImage::GetIJWData(m_pPEAssembly->GetIJWBase()); // If it's already been fixed (in some other appdomain), record the fact and return if (pData->IsFixedUp()) { @@ -4381,7 +4329,7 @@ void Module::FixupVTables() (pFixupTable[iFixup].Type == (COR_VTABLE_PTRSIZED | COR_VTABLE_FROM_UNMANAGED)) || (pFixupTable[iFixup].Type == (COR_VTABLE_PTRSIZED | COR_VTABLE_FROM_UNMANAGED_RETAIN_APPDOMAIN))) { - const BYTE** pPointers = (const BYTE **)m_file->GetVTable(pFixupTable[iFixup].RVA); + const BYTE** pPointers = (const BYTE **)m_pPEAssembly->GetVTable(pFixupTable[iFixup].RVA); for (int iMethod = 0; iMethod < pFixupTable[iFixup].Count; iMethod++) { if (pData->IsMethodFixedUp(iFixup, iMethod)) @@ -4465,7 +4413,7 @@ void Module::FixupVTables() continue; const BYTE** pPointers = (const BYTE **) - m_file->GetVTable(pFixupTable[iFixup].RVA); + m_pPEAssembly->GetVTable(pFixupTable[iFixup].RVA); // Vtables can be 32 or 64 bit. if (pFixupTable[iFixup].Type == COR_VTABLE_PTRSIZED) @@ -4556,7 +4504,7 @@ LoaderHeap *Module::GetDllThunkHeap() MODE_ANY; } CONTRACTL_END; - return PEImage::GetDllThunkHeap(GetFile()->GetIJWBase()); + return PEImage::GetDllThunkHeap(GetPEAssembly()->GetIJWBase()); } @@ -4661,7 +4609,7 @@ IMDInternalImport* Module::GetNativeAssemblyImport(BOOL loadAllowed) } CONTRACT_END; - RETURN GetFile()->GetOpenedILimage()->GetNativeMDImport(loadAllowed); + RETURN GetPEAssembly()->GetPEImage()->GetNativeMDImport(loadAllowed); } BYTE* Module::GetNativeFixupBlobData(RVA rva) @@ -4930,7 +4878,7 @@ HANDLE Module::OpenMethodProfileDataLogFile(GUID mvid) HANDLE profileDataFile = INVALID_HANDLE_VALUE; SString path; - LPCWSTR assemblyPath = m_file->GetPath(); + LPCWSTR assemblyPath = m_pPEAssembly->GetPath(); LPCWSTR ibcDir = g_pConfig->GetZapBBInstrDir(); // should we put the ibc data into a particular directory? if (ibcDir == 0) { path.Set(assemblyPath); // no, then put it beside the IL dll @@ -6402,9 +6350,6 @@ HRESULT Module::WriteMethodProfileDataLogFile(bool cleanup) HRESULT hr = S_OK; - if (IsResource()) - return S_OK; - EX_TRY { if (GetAssembly()->IsInstrumented() && (m_pProfilingBlobTable != NULL) && (m_tokenProfileData != NULL)) @@ -6990,14 +6935,14 @@ idMethodSpec Module::LogInstantiatedMethod(const MethodDesc * md, ULONG flagNum) // =========================================================================== /* static */ -ReflectionModule *ReflectionModule::Create(Assembly *pAssembly, PEFile *pFile, AllocMemTracker *pamTracker, LPCWSTR szName) +ReflectionModule *ReflectionModule::Create(Assembly *pAssembly, PEAssembly *pPEAssembly, AllocMemTracker *pamTracker, LPCWSTR szName) { CONTRACT(ReflectionModule *) { STANDARD_VM_CHECK; PRECONDITION(CheckPointer(pAssembly)); - PRECONDITION(CheckPointer(pFile)); - PRECONDITION(pFile->IsDynamic()); + PRECONDITION(CheckPointer(pPEAssembly)); + PRECONDITION(pPEAssembly->IsDynamic()); POSTCONDITION(CheckPointer(RETVAL)); } CONTRACT_END; @@ -7005,14 +6950,13 @@ ReflectionModule *ReflectionModule::Create(Assembly *pAssembly, PEFile *pFile, A // Hoist CONTRACT into separate routine because of EX incompatibility mdFile token; - _ASSERTE(pFile->IsAssembly()); token = mdFileNil; // Initial memory block for Modules must be zero-initialized (to make it harder // to introduce Destruct crashes arising from OOM's during initialization.) void* pMemory = pamTracker->Track(pAssembly->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(ReflectionModule)))); - ReflectionModuleHolder pModule(new (pMemory) ReflectionModule(pAssembly, token, pFile)); + ReflectionModuleHolder pModule(new (pMemory) ReflectionModule(pAssembly, token, pPEAssembly)); pModule->DoInit(pamTracker, szName); @@ -7025,8 +6969,8 @@ ReflectionModule *ReflectionModule::Create(Assembly *pAssembly, PEFile *pFile, A // The constructor phase initializes just enough so that Destruct() can be safely called. // It cannot throw or fail. // -ReflectionModule::ReflectionModule(Assembly *pAssembly, mdFile token, PEFile *pFile) - : Module(pAssembly, token, pFile) +ReflectionModule::ReflectionModule(Assembly *pAssembly, mdFile token, PEAssembly *pPEAssembly) + : Module(pAssembly, token, pPEAssembly) { CONTRACTL { @@ -7569,9 +7513,9 @@ void Module::EnumMemoryRegions(CLRDataEnumMemoryFlags flags, m_ModuleID->EnumMemoryRegions(flags); } - if (m_file.IsValid()) + if (m_pPEAssembly.IsValid()) { - m_file->EnumMemoryRegions(flags); + m_pPEAssembly->EnumMemoryRegions(flags); } if (m_pAssembly.IsValid()) { @@ -7724,11 +7668,11 @@ LPCWSTR Module::GetPathForErrorMessages() } CONTRACTL_END - PEFile *pFile = GetFile(); + PEAssembly *pPEAssembly = GetPEAssembly(); - if (pFile) + if (pPEAssembly) { - return pFile->GetPathForErrorMessages(); + return pPEAssembly->GetPathForErrorMessages(); } else { diff --git a/src/coreclr/vm/ceeload.h b/src/coreclr/vm/ceeload.h index 2e3b474ef298a..e7dd5629a2c84 100644 --- a/src/coreclr/vm/ceeload.h +++ b/src/coreclr/vm/ceeload.h @@ -19,7 +19,7 @@ #include "corsym.h" #include "typehandle.h" #include "arraylist.h" -#include "pefile.h" +#include "peassembly.h" #include "typehash.h" #include "contractimpl.h" #include "bitmask.h" @@ -43,7 +43,6 @@ #include "ilinstrumentation.h" -class PELoader; class Stub; class MethodDesc; class FieldDesc; @@ -736,7 +735,7 @@ struct ThreadLocalModule; // Native code (NGEN module). A module live in a code:Assembly // // Some important fields are -// * code:Module.m_file - this points at a code:PEFile that understands the layout of a PE file. The most +// * code:Module.m_pPEAssembly - this points at a code:PEAssembly that understands the layout of a PE assembly. The most // important part is getting at the code:Module (see file:..\inc\corhdr.h#ManagedHeader) from there // you can get at the Meta-data and IL) // * code:Module.m_pAvailableClasses - this is a table that lets you look up the types (the code:EEClass) @@ -758,7 +757,7 @@ class Module private: PTR_CUTF8 m_pSimpleName; // Cached simple name for better performance and easier diagnostics - PTR_PEFile m_file; + PTR_PEAssembly m_pPEAssembly; enum { // These are the values set in m_dwTransientFlags. @@ -1079,10 +1078,10 @@ class Module #endif // _DEBUG public: - static Module *Create(Assembly *pAssembly, mdFile kFile, PEFile *pFile, AllocMemTracker *pamTracker); + static Module *Create(Assembly *pAssembly, mdFile kFile, PEAssembly *pPEAssembly, AllocMemTracker *pamTracker); protected: - Module(Assembly *pAssembly, mdFile moduleRef, PEFile *file); + Module(Assembly *pAssembly, mdFile moduleRef, PEAssembly *file); public: @@ -1092,9 +1091,7 @@ class Module PTR_LoaderAllocator GetLoaderAllocator(); - PTR_PEFile GetFile() const { LIMITED_METHOD_DAC_CONTRACT; return m_file; } - - static size_t GetFileOffset() { LIMITED_METHOD_CONTRACT; return offsetof(Module, m_file); } + PTR_PEAssembly GetPEAssembly() const { LIMITED_METHOD_DAC_CONTRACT; return m_pPEAssembly; } BOOL IsManifest(); @@ -1153,10 +1150,8 @@ class Module return m_moduleRef; } - BOOL IsResource() const { WRAPPER_NO_CONTRACT; SUPPORTS_DAC; return GetFile()->IsResource(); } - BOOL IsPEFile() const { WRAPPER_NO_CONTRACT; return !GetFile()->IsDynamic(); } - BOOL IsReflection() const { WRAPPER_NO_CONTRACT; SUPPORTS_DAC; return GetFile()->IsDynamic(); } - BOOL IsIbcOptimized() const { WRAPPER_NO_CONTRACT; return GetFile()->IsIbcOptimized(); } + BOOL IsPEFile() const { WRAPPER_NO_CONTRACT; return !GetPEAssembly()->IsDynamic(); } + BOOL IsReflection() const { WRAPPER_NO_CONTRACT; SUPPORTS_DAC; return GetPEAssembly()->IsDynamic(); } // Returns true iff the debugger can see this module. BOOL IsVisibleToDebugger(); @@ -1170,11 +1165,9 @@ class Module virtual BOOL IsEditAndContinueCapable() const { return FALSE; } - BOOL IsIStream() { LIMITED_METHOD_CONTRACT; return GetFile()->IsIStream(); } - - BOOL IsSystem() { WRAPPER_NO_CONTRACT; SUPPORTS_DAC; return m_file->IsSystem(); } + BOOL IsSystem() { WRAPPER_NO_CONTRACT; SUPPORTS_DAC; return m_pPEAssembly->IsSystem(); } - static BOOL IsEditAndContinueCapable(Assembly *pAssembly, PEFile *file); + static BOOL IsEditAndContinueCapable(Assembly *pAssembly, PEAssembly *file); void EnableEditAndContinue() { @@ -1265,7 +1258,7 @@ class Module return DacGetMDImport(GetReflectionModule(), true); } #endif // DACCESS_COMPILE - return m_file->GetPersistentMDImport(); + return m_pPEAssembly->GetMDImport(); } #ifndef DACCESS_COMPILE @@ -1273,21 +1266,14 @@ class Module { WRAPPER_NO_CONTRACT; - return m_file->GetEmitter(); + return m_pPEAssembly->GetEmitter(); } IMetaDataImport2 *GetRWImporter() { WRAPPER_NO_CONTRACT; - return m_file->GetRWImporter(); - } - - IMetaDataAssemblyImport *GetAssemblyImporter() - { - WRAPPER_NO_CONTRACT; - - return m_file->GetAssemblyImporter(); + return m_pPEAssembly->GetRWImporter(); } HRESULT GetReadablePublicMetaDataInterface(DWORD dwOpenFlags, REFIID riid, LPVOID * ppvInterface); @@ -1379,54 +1365,26 @@ class Module { LIMITED_METHOD_CONTRACT; SUPPORTS_DAC; - { - // IsResource() may lock when accessing metadata, but this is only in debug, - // for the assert below - CONTRACT_VIOLATION(TakesLockViolation); - - _ASSERTE(!IsResource()); - } return m_pAvailableClasses; } #ifndef DACCESS_COMPILE void SetAvailableClassHash(EEClassHashTable *pAvailableClasses) { - LIMITED_METHOD_CONTRACT; - { - // IsResource() may lock when accessing metadata, but this is only in debug, - // for the assert below - CONTRACT_VIOLATION(TakesLockViolation); - _ASSERTE(!IsResource()); - } m_pAvailableClasses = pAvailableClasses; } #endif // !DACCESS_COMPILE PTR_EEClassHashTable GetAvailableClassCaseInsHash() { LIMITED_METHOD_CONTRACT; - SUPPORTS_DAC; - { - // IsResource() may lock when accessing metadata, but this is only in debug, - // for the assert below - CONTRACT_VIOLATION(TakesLockViolation); - _ASSERTE(!IsResource()); - } return m_pAvailableClassesCaseIns; } #ifndef DACCESS_COMPILE void SetAvailableClassCaseInsHash(EEClassHashTable *pAvailableClassesCaseIns) { - LIMITED_METHOD_CONTRACT; - { - // IsResource() may lock when accessing metadata, but this is only in debug, - // for the assert below - CONTRACT_VIOLATION(TakesLockViolation); - _ASSERTE(!IsResource()); - } m_pAvailableClassesCaseIns = pAvailableClassesCaseIns; } #endif // !DACCESS_COMPILE @@ -1436,26 +1394,14 @@ class Module { LIMITED_METHOD_CONTRACT; SUPPORTS_DAC; - { - // IsResource() may lock when accessing metadata, but this is only in debug, - // for the assert below - CONTRACT_VIOLATION(TakesLockViolation); - _ASSERTE(!IsResource()); - } return m_pAvailableParamTypes; } InstMethodHashTable *GetInstMethodHashTable() { LIMITED_METHOD_CONTRACT; - { - // IsResource() may lock when accessing metadata, but this is only in debug, - // for the assert below - CONTRACT_VIOLATION(TakesLockViolation); - _ASSERTE(!IsResource()); - } return m_pInstMethodHashTable; } @@ -1484,9 +1430,9 @@ class Module public: DomainAssembly * LoadAssembly(mdAssemblyRef kAssemblyRef); - Module *GetModuleIfLoaded(mdFile kFile, BOOL onlyLoadedInAppDomain, BOOL loadAllowed); - DomainFile *LoadModule(AppDomain *pDomain, mdFile kFile, BOOL loadResources = TRUE, BOOL bindOnly = FALSE); - PTR_Module LookupModule(mdToken kFile, BOOL loadResources = TRUE); //wrapper over GetModuleIfLoaded, takes modulerefs as well + Module *GetModuleIfLoaded(mdFile kFile); + DomainFile *LoadModule(AppDomain *pDomain, mdFile kFile); + PTR_Module LookupModule(mdToken kFile); //wrapper over GetModuleIfLoaded, takes modulerefs as well DWORD GetAssemblyRefFlags(mdAssemblyRef tkAssemblyRef); // RID maps @@ -1806,8 +1752,8 @@ class Module public: #ifndef DACCESS_COMPILE - BOOL Equals(Module *pModule) { WRAPPER_NO_CONTRACT; return m_file->Equals(pModule->m_file); } - BOOL Equals(PEFile *pFile) { WRAPPER_NO_CONTRACT; return m_file->Equals(pFile); } + BOOL Equals(Module *pModule) { WRAPPER_NO_CONTRACT; return m_pPEAssembly->Equals(pModule->m_pPEAssembly); } + BOOL Equals(PEAssembly *pPEAssembly) { WRAPPER_NO_CONTRACT; return m_pPEAssembly->Equals(pPEAssembly); } #endif // !DACCESS_COMPILE LPCUTF8 GetSimpleName() @@ -1817,11 +1763,11 @@ class Module return m_pSimpleName; } - HRESULT GetScopeName(LPCUTF8 * pszName) { WRAPPER_NO_CONTRACT; return m_file->GetScopeName(pszName); } - const SString &GetPath() { WRAPPER_NO_CONTRACT; return m_file->GetPath(); } + HRESULT GetScopeName(LPCUTF8 * pszName) { WRAPPER_NO_CONTRACT; return m_pPEAssembly->GetScopeName(pszName); } + const SString &GetPath() { WRAPPER_NO_CONTRACT; return m_pPEAssembly->GetPath(); } #ifdef LOGGING - LPCWSTR GetDebugName() { WRAPPER_NO_CONTRACT; return m_file->GetDebugName(); } + LPCWSTR GetDebugName() { WRAPPER_NO_CONTRACT; return m_pPEAssembly->GetDebugName(); } #endif PEImageLayout * GetReadyToRunImage(); @@ -1837,7 +1783,7 @@ class Module CHECK CheckRvaField(RVA field, COUNT_T size); const void *GetInternalPInvokeTarget(RVA target) - { WRAPPER_NO_CONTRACT; return m_file->GetInternalPInvokeTarget(target); } + { WRAPPER_NO_CONTRACT; return m_pPEAssembly->GetInternalPInvokeTarget(target); } BOOL HasTls(); BOOL IsRvaFieldTls(DWORD field); @@ -2268,7 +2214,7 @@ class ReflectionModule : public Module bool m_fSuppressMetadataCapture; #if !defined DACCESS_COMPILE - ReflectionModule(Assembly *pAssembly, mdFile token, PEFile *pFile); + ReflectionModule(Assembly *pAssembly, mdFile token, PEAssembly *pPEAssembly); #endif // !DACCESS_COMPILE public: @@ -2279,7 +2225,7 @@ class ReflectionModule : public Module #endif #if !defined DACCESS_COMPILE - static ReflectionModule *Create(Assembly *pAssembly, PEFile *pFile, AllocMemTracker *pamTracker, LPCWSTR szName); + static ReflectionModule *Create(Assembly *pAssembly, PEAssembly *pPEAssembly, AllocMemTracker *pamTracker, LPCWSTR szName); void Initialize(AllocMemTracker *pamTracker, LPCWSTR szName); void Destruct(); #endif // !DACCESS_COMPILE diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp index 9c68686b31d6e..8f347255d4e0d 100644 --- a/src/coreclr/vm/ceemain.cpp +++ b/src/coreclr/vm/ceemain.cpp @@ -848,7 +848,6 @@ void EEStartupHelper() // Setup the domains. Threads are started in a default domain. // Static initialization - PEAssembly::Attach(); BaseDomain::Attach(); SystemDomain::Attach(); diff --git a/src/coreclr/vm/clrex.cpp b/src/coreclr/vm/clrex.cpp index b4959ad479237..a1fe3e4c8985d 100644 --- a/src/coreclr/vm/clrex.cpp +++ b/src/coreclr/vm/clrex.cpp @@ -1733,7 +1733,7 @@ void DECLSPEC_NORETURN EEFileLoadException::Throw(AssemblySpec *pSpec, HRESULT } /* static */ -void DECLSPEC_NORETURN EEFileLoadException::Throw(PEFile *pFile, HRESULT hr, Exception *pInnerException /* = NULL*/) +void DECLSPEC_NORETURN EEFileLoadException::Throw(PEAssembly *pPEAssembly, HRESULT hr, Exception *pInnerException /* = NULL*/) { CONTRACTL { @@ -1749,11 +1749,8 @@ void DECLSPEC_NORETURN EEFileLoadException::Throw(PEFile *pFile, HRESULT hr, Exc COMPlusThrowOM(); StackSString name; + pPEAssembly->GetDisplayName(name); - if (pFile->IsAssembly()) - ((PEAssembly*)pFile)->GetDisplayName(name); - else - name = StackSString(SString::Utf8, pFile->GetSimpleName()); EX_THROW_WITH_INNER(EEFileLoadException, (name, hr), pInnerException); } diff --git a/src/coreclr/vm/clrex.h b/src/coreclr/vm/clrex.h index 569e8442854cd..cf7d458f90ff3 100644 --- a/src/coreclr/vm/clrex.h +++ b/src/coreclr/vm/clrex.h @@ -18,7 +18,6 @@ class BaseBind; class AssemblySpec; -class PEFile; class PEAssembly; enum StackTraceElementFlags @@ -678,7 +677,7 @@ class EEFileLoadException : public EEException static RuntimeExceptionKind GetFileLoadKind(HRESULT hr); static void DECLSPEC_NORETURN Throw(AssemblySpec *pSpec, HRESULT hr, Exception *pInnerException = NULL); - static void DECLSPEC_NORETURN Throw(PEFile *pFile, HRESULT hr, Exception *pInnerException = NULL); + static void DECLSPEC_NORETURN Throw(PEAssembly *pPEAssembly, HRESULT hr, Exception *pInnerException = NULL); static void DECLSPEC_NORETURN Throw(LPCWSTR path, HRESULT hr, Exception *pInnerException = NULL); static void DECLSPEC_NORETURN Throw(PEAssembly *parent, const void *memory, COUNT_T size, HRESULT hr, Exception *pInnerException = NULL); static BOOL CheckType(Exception* ex); // typeof(EEFileLoadException) diff --git a/src/coreclr/vm/clsload.cpp b/src/coreclr/vm/clsload.cpp index 8b53e019d39aa..9392f6c11d93e 100644 --- a/src/coreclr/vm/clsload.cpp +++ b/src/coreclr/vm/clsload.cpp @@ -654,8 +654,6 @@ void ClassLoader::GetClassValue(NameHandleTable nhTable, Module * pCurrentClsModule = i.GetModule(); PREFIX_ASSUME(pCurrentClsModule != NULL); - if (pCurrentClsModule->IsResource()) - continue; if (pLookInThisModuleOnly && (pCurrentClsModule != pLookInThisModuleOnly)) continue; @@ -844,7 +842,7 @@ void ClassLoader::LazyPopulateCaseSensitiveHashTables() { Module *pModule = i.GetModule(); PREFIX_ASSUME(pModule != NULL); - if (pModule->IsResource() || pModule->GetAvailableClassHash() != NULL) + if (pModule->GetAvailableClassHash() != NULL) continue; // Lazy construction of the case-sensitive hashtable of types is *only* a scenario for ReadyToRun images @@ -858,16 +856,13 @@ void ClassLoader::LazyPopulateCaseSensitiveHashTables() } // Add exported types of the manifest module to the hashtable - if (!GetAssembly()->GetManifestModule()->IsResource()) - { - IMDInternalImport * pManifestImport = GetAssembly()->GetManifestImport(); - HENUMInternalHolder phEnum(pManifestImport); - phEnum.EnumInit(mdtExportedType, mdTokenNil); + IMDInternalImport * pManifestImport = GetAssembly()->GetManifestImport(); + HENUMInternalHolder phEnum(pManifestImport); + phEnum.EnumInit(mdtExportedType, mdTokenNil); - mdToken mdExportedType; - while (pManifestImport->EnumNext(&phEnum, &mdExportedType)) - AddExportedTypeHaveLock(GetAssembly()->GetManifestModule(), mdExportedType, &amTracker); - } + mdToken mdExportedType; + while (pManifestImport->EnumNext(&phEnum, &mdExportedType)) + AddExportedTypeHaveLock(GetAssembly()->GetManifestModule(), mdExportedType, &amTracker); amTracker.SuppressRelease(); } @@ -884,7 +879,7 @@ void ClassLoader::LazyPopulateCaseInsensitiveHashTables() } CONTRACTL_END; - if (!GetAssembly()->GetManifestModule()->IsResource() && GetAssembly()->GetManifestModule()->GetAvailableClassHash() == NULL) + if (GetAssembly()->GetManifestModule()->GetAvailableClassHash() == NULL) { // This is a R2R assembly, and a case insensitive type lookup was triggered. // Construct the case-sensitive table first, since the case-insensitive table @@ -900,9 +895,6 @@ void ClassLoader::LazyPopulateCaseInsensitiveHashTables() while (i.Next()) { Module *pModule = i.GetModule(); - if (pModule->IsResource()) - continue; - if (pModule->GetAvailableClassCaseInsHash() == NULL) { EEClassHashTable *pNewClassCaseInsHash = pModule->GetAvailableClassHash()->MakeCaseInsensitiveTable(pModule, &amTracker); diff --git a/src/coreclr/vm/commodule.cpp b/src/coreclr/vm/commodule.cpp index 99b4982329f04..c00a08336a7c2 100644 --- a/src/coreclr/vm/commodule.cpp +++ b/src/coreclr/vm/commodule.cpp @@ -651,26 +651,13 @@ void QCALLTYPE COMModule::GetScopeName(QCall::ModuleHandle pModule, QCall::Strin BEGIN_QCALL; - LPCSTR szName = NULL; - - if (pModule->IsResource()) - { - IfFailThrow(pModule->GetAssembly()->GetManifestImport()->GetFileProps( - pModule->GetModuleRef(), - &szName, - NULL, - NULL, - NULL)); - } - else + if (!pModule->GetMDImport()->IsValidToken(pModule->GetMDImport()->GetModuleFromScope())) { - if (!pModule->GetMDImport()->IsValidToken(pModule->GetMDImport()->GetModuleFromScope())) - { - ThrowHR(COR_E_BADIMAGEFORMAT); - } - IfFailThrow(pModule->GetMDImport()->GetScopeProps(&szName, 0)); + ThrowHR(COR_E_BADIMAGEFORMAT); } + LPCSTR szName = NULL; + IfFailThrow(pModule->GetMDImport()->GetScopeProps(&szName, 0)); retString.Set(szName); END_QCALL; @@ -734,10 +721,10 @@ HINSTANCE QCALLTYPE COMModule::GetHINSTANCE(QCall::ModuleHandle pModule) // This returns the base address // Other modules should have zero base - PEFile *pPEFile = pModule->GetFile(); - if (!pPEFile->IsDynamic() && !pPEFile->IsResource()) + PEAssembly *pPEAssembly = pModule->GetPEAssembly(); + if (!pPEAssembly->IsDynamic()) { - hMod = (HMODULE) pModule->GetFile()->GetManagedFileContents(); + hMod = (HMODULE) pModule->GetPEAssembly()->GetManagedFileContents(); } //If we don't have an hMod, set it to -1 so that they know that there's none @@ -801,12 +788,6 @@ Object* GetTypesInner(Module* pModule) int AllocSize = 0; MethodTable* pMT = NULL; - if (pModule->IsResource()) - { - refArrClasses = (PTRARRAYREF) AllocateObjectArray(0, CoreLibBinder::GetClass(CLASS__CLASS)); - RETURN(OBJECTREFToObject(refArrClasses)); - } - GCPROTECT_BEGIN(refArrClasses); GCPROTECT_BEGIN(xcept); @@ -890,15 +871,3 @@ Object* GetTypesInner(Module* pModule) RETURN(OBJECTREFToObject(refArrClasses)); } - - -FCIMPL1(FC_BOOL_RET, COMModule::IsResource, ReflectModuleBaseObject* pModuleUNSAFE) -{ - FCALL_CONTRACT; - - if (pModuleUNSAFE == NULL) - FCThrowRes(kArgumentNullException, W("Arg_InvalidHandle")); - - FC_RETURN_BOOL(pModuleUNSAFE->GetModule()->IsResource()); -} -FCIMPLEND diff --git a/src/coreclr/vm/commodule.h b/src/coreclr/vm/commodule.h index c995ec80dfd82..4453214c473a5 100644 --- a/src/coreclr/vm/commodule.h +++ b/src/coreclr/vm/commodule.h @@ -90,8 +90,6 @@ class COMModule static void QCALLTYPE SetModuleName(QCall::ModuleHandle pModule, LPCWSTR wszModuleName); - static FCDECL1(FC_BOOL_RET, IsResource, ReflectModuleBaseObject* pModuleUNSAFE); - static FCDECL1(Object*, GetMethods, ReflectModuleBaseObject* refThisUNSAFE); static diff --git a/src/coreclr/vm/common.h b/src/coreclr/vm/common.h index 7d2f3532219a3..bcd9af84555c6 100644 --- a/src/coreclr/vm/common.h +++ b/src/coreclr/vm/common.h @@ -345,7 +345,7 @@ inline void ClrRestoreNonvolatileContext(PCONTEXT ContextRecord) #include "specialstatics.h" #include "object.h" // We should not really need to put this so early... #include "gchelpers.h" -#include "pefile.h" +#include "peassembly.h" #include "clrex.h" #include "clsload.hpp" // We should not really need to put this so early... #include "siginfo.hpp" @@ -362,7 +362,7 @@ inline void ClrRestoreNonvolatileContext(PCONTEXT ContextRecord) #include "appdomain.hpp" #include "appdomain.inl" #include "assembly.hpp" -#include "pefile.inl" +#include "peassembly.inl" #include "excep.h" #include "method.hpp" #include "field.h" diff --git a/src/coreclr/vm/coreassemblyspec.cpp b/src/coreclr/vm/coreassemblyspec.cpp index 9d1547cda8edd..eaaddafdbe9c6 100644 --- a/src/coreclr/vm/coreassemblyspec.cpp +++ b/src/coreclr/vm/coreassemblyspec.cpp @@ -116,7 +116,7 @@ STDAPI BinderAcquirePEImage(LPCWSTR wszAssemblyPath, { PEImageHolder pImage = PEImage::OpenImage(wszAssemblyPath, MDInternalImport_Default, bundleFileLocation); - // Make sure that the IL image can be opened if the native image is not available. + // Make sure that the IL image can be opened. hr=pImage->TryOpenFile(); if (FAILED(hr)) { @@ -144,7 +144,7 @@ STDAPI BinderAcquireImport(PEImage *pPEImage, EX_TRY { - PEImageLayoutHolder pLayout(pPEImage->GetLayout(PEImageLayout::LAYOUT_ANY,PEImage::LAYOUT_CREATEIFNEEDED)); + PEImageLayout* pLayout = pPEImage->GetOrCreateLayout(PEImageLayout::LAYOUT_ANY); // CheckCorHeader includes check of NT headers too if (!pLayout->CheckCorHeader()) diff --git a/src/coreclr/vm/corhost.cpp b/src/coreclr/vm/corhost.cpp index a4b2bfa74a876..861ec15be77a8 100644 --- a/src/coreclr/vm/corhost.cpp +++ b/src/coreclr/vm/corhost.cpp @@ -612,8 +612,6 @@ HRESULT CorHost2::CreateAppDomainWithManager( if (dwFlags & APPDOMAIN_FORCE_TRIVIAL_WAIT_OPERATIONS) pDomain->SetForceTrivialWaitOperations(); - pDomain->CreateBinderContext(); - { GCX_COOP(); diff --git a/src/coreclr/vm/debugdebugger.cpp b/src/coreclr/vm/debugdebugger.cpp index e21939990405c..e6313d9c20be9 100644 --- a/src/coreclr/vm/debugdebugger.cpp +++ b/src/coreclr/vm/debugdebugger.cpp @@ -713,11 +713,11 @@ FCIMPL4(void, DebugStackTrace::GetStackFramesInternal, I4 *pMethodToken = (I4 *)((I4ARRAYREF)pStackFrameHelper->rgiMethodToken)->GetDirectPointerToNonObjectElements(); pMethodToken[iNumValidFrames] = pMethod->GetMemberDef(); - PEFile *pPEFile = pModule->GetFile(); + PEAssembly *pPEAssembly = pModule->GetPEAssembly(); // Get the address and size of the loaded PE image COUNT_T peSize; - PTR_CVOID peAddress = pPEFile->GetLoadedImageContents(&peSize); + PTR_CVOID peAddress = pPEAssembly->GetLoadedImageContents(&peSize); // Save the PE address and size PTR_CVOID *pLoadedPeAddress = (PTR_CVOID *)pStackFrameHelper->rgLoadedPeAddress->GetDataPtr(); @@ -727,11 +727,11 @@ FCIMPL4(void, DebugStackTrace::GetStackFramesInternal, pLoadedPeSize[iNumValidFrames] = (I4)peSize; // Set flag indicating PE file in memory has the on disk layout - if (!pPEFile->IsDynamic()) + if (!pPEAssembly->IsDynamic()) { // This flag is only available for non-dynamic assemblies. U1 *pIsFileLayout = (U1 *)((BOOLARRAYREF)pStackFrameHelper->rgiIsFileLayout)->GetDirectPointerToNonObjectElements(); - pIsFileLayout[iNumValidFrames] = (U1) pPEFile->GetLoaded()->IsFlat(); + pIsFileLayout[iNumValidFrames] = (U1) pPEAssembly->GetLoadedLayout()->IsFlat(); } // If there is a in memory symbol stream @@ -750,7 +750,7 @@ FCIMPL4(void, DebugStackTrace::GetStackFramesInternal, else { // Set the pdb path (assembly file name) - SString assemblyPath = pPEFile->GetIdentityPath(); + SString assemblyPath = pPEAssembly->GetIdentityPath(); if (!assemblyPath.IsEmpty()) { OBJECTREF obj = (OBJECTREF)StringObject::NewString(assemblyPath); diff --git a/src/coreclr/vm/domainfile.cpp b/src/coreclr/vm/domainfile.cpp index 70f03913f1879..ca9ea6565d3f7 100644 --- a/src/coreclr/vm/domainfile.cpp +++ b/src/coreclr/vm/domainfile.cpp @@ -30,10 +30,9 @@ #endif // FEATURE_PERFMAP #ifndef DACCESS_COMPILE -DomainFile::DomainFile(AppDomain *pDomain, PEFile *pFile) +DomainFile::DomainFile(AppDomain *pDomain, PEAssembly *pPEAssembly) : m_pDomain(pDomain), - m_pFile(pFile), - m_pOriginalFile(NULL), + m_pPEAssembly(pPEAssembly), m_pModule(NULL), m_level(FILE_LOAD_CREATE), m_pError(NULL), @@ -54,7 +53,7 @@ DomainFile::DomainFile(AppDomain *pDomain, PEFile *pFile) CONTRACTL_END; m_hExposedModuleObject = NULL; - pFile->AddRef(); + pPEAssembly->AddRef(); } DomainFile::~DomainFile() @@ -68,9 +67,7 @@ DomainFile::~DomainFile() } CONTRACTL_END; - m_pFile->Release(); - if(m_pOriginalFile) - m_pOriginalFile->Release(); + m_pPEAssembly->Release(); if (m_pDynamicMethodTable) m_pDynamicMethodTable->Destroy(); delete m_pError; @@ -282,10 +279,10 @@ CHECK DomainFile::CheckLoaded() // assemblies for bootstrapping purposes. This is because it has no // dependencies, security checks, and doesn't rely on loader notifications. - if (GetFile()->IsSystem()) + if (GetPEAssembly()->IsSystem()) CHECK_OK; - CHECK_MSG(GetFile()->CheckLoaded(), "PEFile has not been loaded"); + CHECK_MSG(GetPEAssembly()->IsLoaded(), "PEAssembly has not been loaded"); CHECK_OK; } @@ -310,10 +307,10 @@ CHECK DomainFile::CheckActivated() // assemblies for bootstrapping purposes. This is because it has no // dependencies, security checks, and doesn't rely on loader notifications. - if (GetFile()->IsSystem()) + if (GetPEAssembly()->IsSystem()) CHECK_OK; - CHECK_MSG(GetFile()->CheckLoaded(), "PEFile has not been loaded"); + CHECK_MSG(GetPEAssembly()->IsLoaded(), "PEAssembly has not been loaded"); CHECK_MSG(IsLoaded(), "DomainFile has not been fully loaded"); CHECK_MSG(m_bDisableActivationCheck || CheckLoadLevel(FILE_ACTIVE), "File has not had execution verified"); @@ -392,7 +389,7 @@ OBJECTREF DomainFile::GetExposedModuleObject() GCPROTECT_BEGIN(refClass); - if (GetFile()->IsDynamic()) + if (GetPEAssembly()->IsDynamic()) { refClass = (REFLECTMODULEBASEREF) AllocateObject(CoreLibBinder::GetClass(CLASS__MODULE_BUILDER)); } @@ -528,7 +525,7 @@ void DomainFile::LoadLibrary() } CONTRACTL_END; - GetFile()->LoadLibrary(); + GetPEAssembly()->EnsureLoaded(); } void DomainFile::PostLoadLibrary() @@ -536,8 +533,8 @@ void DomainFile::PostLoadLibrary() CONTRACTL { INSTANCE_CHECK; - // Note that GetFile()->LoadLibrary must be called before this OUTSIDE OF THE LOCKS - PRECONDITION(GetFile()->CheckLoaded()); + // Note that GetPEAssembly()->EnsureLoaded must be called before this OUTSIDE OF THE LOCKS + PRECONDITION(GetPEAssembly()->IsLoaded()); STANDARD_VM_CHECK; } CONTRACTL_END; @@ -597,8 +594,7 @@ void DomainFile::VtableFixups() { WRAPPER_NO_CONTRACT; - if (!GetCurrentModule()->IsResource()) - GetCurrentModule()->FixupVTables(); + GetCurrentModule()->FixupVTables(); } void DomainFile::FinishLoad() @@ -622,7 +618,7 @@ void DomainFile::FinishLoad() #ifdef FEATURE_PERFMAP // Notify the perfmap of the IL image load. - PerfMap::LogImageLoad(m_pFile); + PerfMap::LogImageLoad(m_pPEAssembly); #endif } @@ -682,8 +678,8 @@ void DomainFile::Activate() // DomainAssembly //-------------------------------------------------------------------------------- -DomainAssembly::DomainAssembly(AppDomain *pDomain, PEFile *pFile, LoaderAllocator *pLoaderAllocator) - : DomainFile(pDomain, pFile), +DomainAssembly::DomainAssembly(AppDomain *pDomain, PEAssembly *pPEAssembly, LoaderAllocator *pLoaderAllocator) + : DomainFile(pDomain, pPEAssembly), m_pAssembly(NULL), m_debuggerFlags(DACF_NONE), m_fDebuggerUnloadStarted(FALSE), @@ -700,7 +696,7 @@ DomainAssembly::DomainAssembly(AppDomain *pDomain, PEFile *pFile, LoaderAllocato } CONTRACTL_END; - pFile->ValidateForExecution(); + pPEAssembly->ValidateForExecution(); // !!! backout @@ -747,8 +743,9 @@ void DomainAssembly::SetAssembly(Assembly* pAssembly) { STANDARD_VM_CONTRACT; - UpdatePEFile(pAssembly->GetManifestFile()); - _ASSERTE(pAssembly->GetManifestModule()->GetFile()==m_pFile); + _ASSERTE(pAssembly->GetManifestModule()->GetPEAssembly()==m_pPEAssembly); + _ASSERTE(m_pAssembly == NULL); + m_pAssembly = pAssembly; m_pModule = pAssembly->GetManifestModule(); @@ -793,7 +790,7 @@ OBJECTREF DomainAssembly::GetExposedAssemblyObject() { ASSEMBLYREF assemblyObj = NULL; MethodTable * pMT; - if (GetFile()->IsDynamic()) + if (GetPEAssembly()->IsDynamic()) { // This is unnecessary because the managed InternalAssemblyBuilder object // should have already been created at the time of DefineDynamicAssembly @@ -879,18 +876,11 @@ void DomainAssembly::Allocate() { //! If you decide to remove "if" do not remove this brace: order is important here - in the case of an exception, //! the Assembly holder must destruct before the AllocMemTracker declared above. - - // We can now rely on the fact that our MDImport will not change so we can stop refcounting it. - GetFile()->MakeMDImportPersistent(); - NewHolder assemblyHolder(NULL); - assemblyHolder = pAssembly = Assembly::Create(m_pDomain, GetFile(), GetDebuggerInfoBits(), this->IsCollectible(), pamTracker, this->IsCollectible() ? this->GetLoaderAllocator() : NULL); + assemblyHolder = pAssembly = Assembly::Create(m_pDomain, GetPEAssembly(), GetDebuggerInfoBits(), this->IsCollectible(), pamTracker, this->IsCollectible() ? this->GetLoaderAllocator() : NULL); assemblyHolder->SetIsTenured(); - //@todo! This is too early to be calling SuppressRelease. The right place to call it is below after - // the CANNOTTHROWCOMPLUSEXCEPTION. Right now, we have to do this to unblock OOM injection testing quickly - // as doing the right thing is nontrivial. pamTracker->SuppressRelease(); assemblyHolder.SuppressRelease(); } @@ -973,7 +963,7 @@ BOOL DomainAssembly::GetResource(LPCSTR szName, DWORD *cbResource, } CONTRACTL_END; - return GetFile()->GetResource( szName, + return GetPEAssembly()->GetResource( szName, cbResource, pbInMemoryResource, pAssemblyRef, @@ -1048,7 +1038,7 @@ HRESULT DomainAssembly::GetDebuggingCustomAttributes(DWORD *pdwFlags) ULONG size; BYTE *blob; mdModule mdMod; - ReleaseHolder mdImport(GetFile()->GetMDImportWithRef()); + IMDInternalImport* mdImport = GetPEAssembly()->GetMDImport(); mdMod = mdImport->GetModuleFromScope(); mdAssembly asTK = TokenFromRid(mdtAssembly, 1); @@ -1202,9 +1192,9 @@ DomainFile::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) // so we don't need to duplicate effort; thus we do noting with m_pModule. // For MiniDumpNormal, we only want the file name. - if (m_pFile.IsValid()) + if (m_pPEAssembly.IsValid()) { - m_pFile->EnumMemoryRegions(flags); + m_pPEAssembly->EnumMemoryRegions(flags); } if (flags != CLRDATA_ENUM_MEM_MINI && flags != CLRDATA_ENUM_MEM_TRIAGE diff --git a/src/coreclr/vm/domainfile.h b/src/coreclr/vm/domainfile.h index 4396ad6c8d990..49a8344b21e84 100644 --- a/src/coreclr/vm/domainfile.h +++ b/src/coreclr/vm/domainfile.h @@ -87,23 +87,16 @@ class DomainFile return m_pDomain; } - PEFile *GetFile() + PEAssembly *GetPEAssembly() { LIMITED_METHOD_DAC_CONTRACT; - return m_pFile; + return PTR_PEAssembly(m_pPEAssembly); } - PEFile *GetOriginalFile() - { - LIMITED_METHOD_DAC_CONTRACT; - return m_pOriginalFile!= NULL ? m_pOriginalFile : m_pFile; - } - - IMDInternalImport *GetMDImport() { WRAPPER_NO_CONTRACT; - return m_pFile->GetPersistentMDImport(); + return m_pPEAssembly->GetMDImport(); } OBJECTREF GetExposedModuleObjectIfExists() @@ -120,20 +113,20 @@ class DomainFile BOOL IsSystem() { WRAPPER_NO_CONTRACT; - return GetFile()->IsSystem(); + return GetPEAssembly()->IsSystem(); } LPCUTF8 GetSimpleName() { WRAPPER_NO_CONTRACT; - return GetFile()->GetSimpleName(); + return GetPEAssembly()->GetSimpleName(); } #ifdef LOGGING LPCWSTR GetDebugName() { WRAPPER_NO_CONTRACT; - return GetFile()->GetDebugName(); + return GetPEAssembly()->GetDebugName(); } #endif @@ -252,8 +245,8 @@ class DomainFile // ------------------------------------------------------------ #ifndef DACCESS_COMPILE - BOOL Equals(DomainFile *pFile) { WRAPPER_NO_CONTRACT; return GetFile()->Equals(pFile->GetFile()); } - BOOL Equals(PEFile *pFile) { WRAPPER_NO_CONTRACT; return GetFile()->Equals(pFile); } + BOOL Equals(DomainFile *pFile) { WRAPPER_NO_CONTRACT; return GetPEAssembly()->Equals(pFile->GetPEAssembly()); } + BOOL Equals(PEAssembly *pPEAssembly) { WRAPPER_NO_CONTRACT; return GetPEAssembly()->Equals(pPEAssembly); } #endif // DACCESS_COMPILE Module* GetCurrentModule(); @@ -279,7 +272,7 @@ class DomainFile friend class Module; friend class FileLoadLock; - DomainFile(AppDomain *pDomain, PEFile *pFile); + DomainFile(AppDomain *pDomain, PEAssembly *pPEAssembly); BOOL DoIncrementalLoad(FileLoadLevel targetLevel); void ClearLoading() { LIMITED_METHOD_CONTRACT; m_loading = FALSE; } @@ -306,17 +299,13 @@ class DomainFile void SetProfilerNotified() { LIMITED_METHOD_CONTRACT; m_notifyflags|= PROFILER_NOTIFIED; } void SetDebuggerNotified() { LIMITED_METHOD_CONTRACT; m_notifyflags|=DEBUGGER_NOTIFIED; } void SetShouldNotifyDebugger() { LIMITED_METHOD_CONTRACT; m_notifyflags|=DEBUGGER_NEEDNOTIFICATION; } -#ifndef DACCESS_COMPILE - void UpdatePEFileWorker(PTR_PEFile pFile); -#endif // ------------------------------------------------------------ // Instance data // ------------------------------------------------------------ PTR_AppDomain m_pDomain; - PTR_PEFile m_pFile; - PTR_PEFile m_pOriginalFile; // keep file alive just in case someone is sitill using it. If this is not NULL then m_pFile contains reused file from the shared assembly + PTR_PEAssembly m_pPEAssembly; PTR_Module m_pModule; FileLoadLevel m_level; LOADERHANDLE m_hExposedModuleObject; @@ -425,12 +414,6 @@ class DomainAssembly : public DomainFile // Public API // ------------------------------------------------------------ - PEAssembly *GetFile() - { - LIMITED_METHOD_CONTRACT; - return PTR_PEAssembly(m_pFile); - } - LoaderAllocator *GetLoaderAllocator() { LIMITED_METHOD_CONTRACT; @@ -592,7 +575,7 @@ class DomainAssembly : public DomainFile public: ~DomainAssembly(); private: - DomainAssembly(AppDomain *pDomain, PEFile *pFile, LoaderAllocator *pLoaderAllocator); + DomainAssembly(AppDomain *pDomain, PEAssembly *pPEAssembly, LoaderAllocator *pLoaderAllocator); #endif // ------------------------------------------------------------ @@ -606,10 +589,6 @@ class DomainAssembly : public DomainFile void DeliverAsyncEvents(); #endif - void UpdatePEFile(PTR_PEFile pFile); - - BOOL IsInstrumented(); - public: ULONG HashIdentity(); diff --git a/src/coreclr/vm/domainfile.inl b/src/coreclr/vm/domainfile.inl index 91ed0ae4a1ff3..a87d9aaef1309 100644 --- a/src/coreclr/vm/domainfile.inl +++ b/src/coreclr/vm/domainfile.inl @@ -58,39 +58,10 @@ inline Assembly* DomainAssembly::GetAssembly() return m_pAssembly; } -#ifndef DACCESS_COMPILE -inline void DomainFile::UpdatePEFileWorker(PTR_PEFile pFile) -{ - LIMITED_METHOD_CONTRACT; - CONSISTENCY_CHECK(CheckPointer(pFile)); - if (pFile==m_pFile) - return; - _ASSERTE(m_pOriginalFile==NULL); - m_pOriginalFile=m_pFile; - pFile->AddRef(); - m_pFile=pFile; -} - -inline void DomainAssembly::UpdatePEFile(PTR_PEFile pFile) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - CAN_TAKE_LOCK; - } - CONTRACTL_END; - - GetAppDomain()->UpdatePublishHostedAssembly(this, pFile); -} - -#endif // DACCESS_COMPILE - inline ULONG DomainAssembly::HashIdentity() { WRAPPER_NO_CONTRACT; - return GetFile()->HashIdentity(); + return GetPEAssembly()->HashIdentity(); } inline BOOL DomainAssembly::IsCollectible() diff --git a/src/coreclr/vm/dwbucketmanager.hpp b/src/coreclr/vm/dwbucketmanager.hpp index cf360ac7322a5..21267b8f36070 100644 --- a/src/coreclr/vm/dwbucketmanager.hpp +++ b/src/coreclr/vm/dwbucketmanager.hpp @@ -701,14 +701,14 @@ void BaseBucketParamsManager::GetModuleTimeStamp(__out_ecount(maxLength) WCHAR* { // We only store the IL timestamp in the native image for the // manifest module. We should consider fixing this for Orcas. - PTR_PEFile pFile = pModule->GetAssembly()->GetManifestModule()->GetFile(); + PTR_PEAssembly pFile = pModule->GetAssembly()->GetManifestModule()->GetPEAssembly(); // for dynamic modules use 0 as the time stamp ULONG ulTimeStamp = 0; if (!pFile->IsDynamic()) { - ulTimeStamp = pFile->GetILImageTimeDateStamp(); + ulTimeStamp = pFile->GetPEImageTimeDateStamp(); _ASSERTE(ulTimeStamp != 0); } @@ -957,13 +957,13 @@ bool BaseBucketParamsManager::GetFileVersionInfoForModule(Module* pModule, USHOR bool succeeded = false; - PEFile* pFile = pModule->GetFile(); - if (pFile) + PEAssembly* pPEAssembly = pModule->GetPEAssembly(); + if (pPEAssembly) { // if we failed to get the version info from the native image then fall back to the IL image. if (!succeeded) { - LPCWSTR modulePath = pFile->GetPath().GetUnicode(); + LPCWSTR modulePath = pPEAssembly->GetPath().GetUnicode(); if (modulePath != NULL && modulePath != SString::Empty() && SUCCEEDED(DwGetFileVersionInfo(modulePath, major, minor, build, revision))) { succeeded = true; diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index a93343b112a41..5abf6c7d87c9a 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -314,7 +314,6 @@ FCFuncStart(gCOMModuleFuncs) QCFuncElement("GetScopeName", COMModule::GetScopeName) FCFuncElement("GetTypes", COMModule::GetTypes) QCFuncElement("GetFullyQualifiedName", COMModule::GetFullyQualifiedName) - FCFuncElement("IsResource", COMModule::IsResource) FCFuncEnd() FCFuncStart(gCOMModuleBuilderFuncs) diff --git a/src/coreclr/vm/eedbginterfaceimpl.h b/src/coreclr/vm/eedbginterfaceimpl.h index d4d790c9e886f..72594c336794c 100644 --- a/src/coreclr/vm/eedbginterfaceimpl.h +++ b/src/coreclr/vm/eedbginterfaceimpl.h @@ -26,7 +26,7 @@ #include "debugdebugger.h" #include "eeconfig.h" -#include "pefile.h" +#include "peassembly.h" class EEDbgInterfaceImpl : public EEDebugInterface { diff --git a/src/coreclr/vm/encee.cpp b/src/coreclr/vm/encee.cpp index b11e2d8437068..3f745d299427c 100644 --- a/src/coreclr/vm/encee.cpp +++ b/src/coreclr/vm/encee.cpp @@ -35,8 +35,8 @@ static int g_BreakOnEnCResolveField = -1; // The constructor phase initializes just enough so that Destruct() can be safely called. // It cannot throw or fail. // -EditAndContinueModule::EditAndContinueModule(Assembly *pAssembly, mdToken moduleRef, PEFile *file) - : Module(pAssembly, moduleRef, file) +EditAndContinueModule::EditAndContinueModule(Assembly *pAssembly, mdToken moduleRef, PEAssembly *pPEAssembly) + : Module(pAssembly, moduleRef, pPEAssembly) { CONTRACTL { @@ -175,7 +175,7 @@ HRESULT EditAndContinueModule::ApplyEditAndContinue( { // ConvertMDInternalToReadWrite should only ever be called on EnC capable files. _ASSERTE(IsEditAndContinueCapable()); // this also checks that the file is EnC capable - GetFile()->ConvertMDInternalToReadWrite(); + GetPEAssembly()->ConvertMDInternalToReadWrite(); } EX_CATCH_HRESULT(hr); diff --git a/src/coreclr/vm/encee.h b/src/coreclr/vm/encee.h index 2c7a834f32803..bd20a4057651a 100644 --- a/src/coreclr/vm/encee.h +++ b/src/coreclr/vm/encee.h @@ -202,13 +202,13 @@ class EditAndContinueModule : public Module // Return the minimum permissable address for new IL to be stored at // This can't be less than the current load address because then we'd // have negative RVAs. - BYTE *GetEnCBase() { return (BYTE *) GetFile()->GetManagedFileContents(); } + BYTE *GetEnCBase() { return (BYTE *) GetPEAssembly()->GetManagedFileContents(); } #endif // DACCESS_COMPILE private: // Constructor is invoked only by Module::Create - friend Module *Module::Create(Assembly *pAssembly, mdToken moduleRef, PEFile *file, AllocMemTracker *pamTracker); - EditAndContinueModule(Assembly *pAssembly, mdToken moduleRef, PEFile *file); + friend Module *Module::Create(Assembly *pAssembly, mdToken moduleRef, PEAssembly *pPEAssembly, AllocMemTracker *pamTracker); + EditAndContinueModule(Assembly *pAssembly, mdToken moduleRef, PEAssembly *pPEAssembly); protected: #ifndef DACCESS_COMPILE diff --git a/src/coreclr/vm/eventtrace.cpp b/src/coreclr/vm/eventtrace.cpp index dd36960e257e5..c549018584d70 100644 --- a/src/coreclr/vm/eventtrace.cpp +++ b/src/coreclr/vm/eventtrace.cpp @@ -6135,13 +6135,13 @@ static void GetCodeViewInfo(Module * pModule, CV_INFO_PDB70 * pCvInfoIL, CV_INFO ZeroMemory(pCvInfoIL, sizeof(*pCvInfoIL)); ZeroMemory(pCvInfoNative, sizeof(*pCvInfoNative)); - PTR_PEFile pPEFile = pModule->GetFile(); - _ASSERTE(pPEFile != NULL); + PTR_PEAssembly pPEAssembly = pModule->GetPEAssembly(); + _ASSERTE(pPEAssembly != NULL); PTR_PEImageLayout pLayout = NULL; - if (pPEFile->HasOpenedILimage()) + if (pPEAssembly->HasPEImage()) { - pLayout = pPEFile->GetLoadedIL(); + pLayout = pPEAssembly->GetLoadedLayout(); } if (pLayout == NULL) @@ -6338,7 +6338,7 @@ VOID ETW::LoaderLog::SendModuleEvent(Module *pModule, DWORD dwEventOptions, BOOL if(!bIsDynamicAssembly) { - ModuleILPath = (PWCHAR)pModule->GetAssembly()->GetManifestFile()->GetILimage()->GetPath().GetUnicode(); + ModuleILPath = (PWCHAR)pModule->GetAssembly()->GetManifestFile()->GetPEImage()->GetPath().GetUnicode(); ModuleNativePath = (PWCHAR)pEmptyString; } diff --git a/src/coreclr/vm/exceptmacros.h b/src/coreclr/vm/exceptmacros.h index 1eba8ca9c47fc..70e1fe0871e0d 100644 --- a/src/coreclr/vm/exceptmacros.h +++ b/src/coreclr/vm/exceptmacros.h @@ -520,7 +520,7 @@ VOID ThrowBadFormatWorkerT(UINT resID, T * pImgObj DEBUGARG(__in_z const char *c // Worker macro for throwing BadImageFormat exceptions. // // resID: resource ID in mscorrc.rc. Message may not have substitutions. resID is permitted (but not encouraged) to be 0. -// imgObj: one of Module* or PEFile* or PEImage* (must support GetPathForErrorMessages method.) +// imgObj: one of Module* or PEAssembly* or PEImage* (must support GetPathForErrorMessages method.) // #define IfFailThrowBF(hresult, resID, imgObj) \ do \ diff --git a/src/coreclr/vm/inlinetracking.cpp b/src/coreclr/vm/inlinetracking.cpp index bfd420850c1bd..86d73ea0a7657 100644 --- a/src/coreclr/vm/inlinetracking.cpp +++ b/src/coreclr/vm/inlinetracking.cpp @@ -45,7 +45,7 @@ bool MethodInModule::operator <(const MethodInModule& other) const } else { - m_module->GetFile()->GetMVID(&thisGuid); + m_module->GetPEAssembly()->GetMVID(&thisGuid); } if (other.m_module == NULL) @@ -54,7 +54,7 @@ bool MethodInModule::operator <(const MethodInModule& other) const } else { - other.m_module->GetFile()->GetMVID(&otherGuid); + other.m_module->GetPEAssembly()->GetMVID(&otherGuid); } return memcmp(&thisGuid, &otherGuid, sizeof(GUID)) < 0; diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 9b6023b4d98d0..3ffb87896fa96 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -984,7 +984,7 @@ void CEEInfo::resolveToken(/* IN, OUT */ CORINFO_RESOLVED_TOKEN * pResolvedToken ThrowBadTokenException(pResolvedToken); { - DomainFile *pTargetModule = pModule->LoadModule(GetAppDomain(), metaTOK, FALSE /* loadResources */); + DomainFile *pTargetModule = pModule->LoadModule(GetAppDomain(), metaTOK); if (pTargetModule == NULL) COMPlusThrowHR(COR_E_BADIMAGEFORMAT); th = TypeHandle(pTargetModule->GetModule()->GetGlobalMethodTable()); @@ -7798,14 +7798,14 @@ void CEEInfo::reportInliningDecision (CORINFO_METHOD_HANDLE inlinerHnd, if (LoggingOn(LF_JIT, LL_INFO100000)) { SString currentMethodName; - currentMethodName.AppendUTF8(m_pMethodBeingCompiled->GetModule_NoLogging()->GetFile()->GetSimpleName()); + currentMethodName.AppendUTF8(m_pMethodBeingCompiled->GetModule_NoLogging()->GetPEAssembly()->GetSimpleName()); currentMethodName.Append(L'/'); TypeString::AppendMethodInternal(currentMethodName, m_pMethodBeingCompiled, TypeString::FormatBasic); SString inlineeMethodName; if (GetMethod(inlineeHnd)) { - inlineeMethodName.AppendUTF8(GetMethod(inlineeHnd)->GetModule_NoLogging()->GetFile()->GetSimpleName()); + inlineeMethodName.AppendUTF8(GetMethod(inlineeHnd)->GetModule_NoLogging()->GetPEAssembly()->GetSimpleName()); inlineeMethodName.Append(L'/'); TypeString::AppendMethodInternal(inlineeMethodName, GetMethod(inlineeHnd), TypeString::FormatBasic); } @@ -7817,7 +7817,7 @@ void CEEInfo::reportInliningDecision (CORINFO_METHOD_HANDLE inlinerHnd, SString inlinerMethodName; if (GetMethod(inlinerHnd)) { - inlinerMethodName.AppendUTF8(GetMethod(inlinerHnd)->GetModule_NoLogging()->GetFile()->GetSimpleName()); + inlinerMethodName.AppendUTF8(GetMethod(inlinerHnd)->GetModule_NoLogging()->GetPEAssembly()->GetSimpleName()); inlinerMethodName.Append(L'/'); TypeString::AppendMethodInternal(inlinerMethodName, GetMethod(inlinerHnd), TypeString::FormatBasic); } @@ -12815,7 +12815,7 @@ PCODE UnsafeJitFunction(PrepareCodeConfig* config, LARGE_INTEGER methodJitTimeStop; QueryPerformanceCounter(&methodJitTimeStop); SString codeBase; - ftn->GetModule()->GetDomainFile()->GetFile()->GetCodeBaseOrName(codeBase); + ftn->GetModule()->GetDomainFile()->GetPEAssembly()->GetPathOrCodeBase(codeBase); codeBase.AppendPrintf(W(",0x%x,%d,%d\n"), //(const WCHAR *)codeBase, //module name ftn->GetMemberDef(), //method token diff --git a/src/coreclr/vm/loaderallocator.cpp b/src/coreclr/vm/loaderallocator.cpp index f30f3f83daecd..d92fba38536e5 100644 --- a/src/coreclr/vm/loaderallocator.cpp +++ b/src/coreclr/vm/loaderallocator.cpp @@ -481,9 +481,9 @@ LoaderAllocator * LoaderAllocator::GCLoaderAllocators_RemoveAssemblies(AppDomain if (!domainAssemblyToRemove->GetAssembly()->IsDynamic()) { - pAppDomain->RemoveFileFromCache(domainAssemblyToRemove->GetFile()); + pAppDomain->RemoveFileFromCache(domainAssemblyToRemove->GetPEAssembly()); AssemblySpec spec; - spec.InitializeSpec(domainAssemblyToRemove->GetFile()); + spec.InitializeSpec(domainAssemblyToRemove->GetPEAssembly()); VERIFY(pAppDomain->RemoveAssemblyFromCache(domainAssemblyToRemove)); } diff --git a/src/coreclr/vm/memberload.cpp b/src/coreclr/vm/memberload.cpp index ce53bf22589f8..78fdb518a1618 100644 --- a/src/coreclr/vm/memberload.cpp +++ b/src/coreclr/vm/memberload.cpp @@ -277,7 +277,7 @@ void MemberLoader::GetDescFromMemberRef(Module * pModule, { case mdtModuleRef: { - DomainFile *pTargetModule = pModule->LoadModule(GetAppDomain(), parent, FALSE /* loadResources */); + DomainFile *pTargetModule = pModule->LoadModule(GetAppDomain(), parent); if (pTargetModule == NULL) COMPlusThrowHR(COR_E_BADIMAGEFORMAT); typeHnd = TypeHandle(pTargetModule->GetModule()->GetGlobalMethodTable()); diff --git a/src/coreclr/vm/methoditer.cpp b/src/coreclr/vm/methoditer.cpp index 5bd7e81f5de2d..841b3214d2e2d 100644 --- a/src/coreclr/vm/methoditer.cpp +++ b/src/coreclr/vm/methoditer.cpp @@ -83,9 +83,6 @@ BOOL LoadedMethodDescIterator::Next( if (!m_moduleIterator.Next()) goto ADVANCE_ASSEMBLY; - if (GetCurrentModule()->IsResource()) - goto ADVANCE_MODULE; - if (m_mainMD->HasClassInstantiation()) { m_typeIterator.Reset(); diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index 8751224545933..90f451a1dfde1 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -4337,9 +4337,8 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList, DWORD rva; IfFailThrow(pInternalImport->GetFieldRVA(pFD->GetMemberDef(), &rva)); - // Ensure that the IL image is loaded. Note that this assembly may - // have an ngen image, but this type may have failed to load during ngen. - GetModule()->GetFile()->LoadLibrary(FALSE); + // Ensure that the IL image is loaded. + GetModule()->GetPEAssembly()->EnsureLoaded(); DWORD fldSize; if (FieldDescElementType == ELEMENT_TYPE_VALUETYPE) @@ -11216,7 +11215,7 @@ BOOL MethodTableBuilder::NeedsAlignedBaseOffset() // Always use the ReadyToRun field layout algorithm if the source IL image was ReadyToRun, independent on // whether ReadyToRun is actually enabled for the module. It is required to allow mixing and matching // ReadyToRun images with and without input bubble enabled. - if (!GetModule()->GetFile()->IsReadyToRun()) + if (!GetModule()->GetPEAssembly()->IsReadyToRun()) { // Always use ReadyToRun field layout algorithm to produce ReadyToRun images return FALSE; diff --git a/src/coreclr/vm/multicorejit.cpp b/src/coreclr/vm/multicorejit.cpp index b94be184959d7..e66e6e39d8f49 100644 --- a/src/coreclr/vm/multicorejit.cpp +++ b/src/coreclr/vm/multicorejit.cpp @@ -255,25 +255,20 @@ bool ModuleVersion::GetModuleVersion(Module * pModule) // GetMVID can throw exception EX_TRY { - PEFile * pFile = pModule->GetFile(); + PEAssembly * pAsm = pModule->GetPEAssembly(); - if (pFile != NULL) + if (pAsm != NULL) { - PEAssembly * pAsm = pFile->GetAssembly(); + // CorAssemblyFlags, only 16-bit used + versionFlags = pAsm->GetFlags(); - if (pAsm != NULL) - { - // CorAssemblyFlags, only 16-bit used - versionFlags = pAsm->GetFlags(); - - _ASSERTE((versionFlags & 0x80000000) == 0); + _ASSERTE((versionFlags & 0x80000000) == 0); - pAsm->GetVersion(& major, & minor, & build, & revision); + pAsm->GetVersion(&major, &minor, &build, &revision); - pAsm->GetMVID(& mvid); + pAsm->GetMVID(&mvid); - hr = S_OK; - } + hr = S_OK; } // If the load context is LOADFROM, store it in the flags. diff --git a/src/coreclr/vm/multicorejitplayer.cpp b/src/coreclr/vm/multicorejitplayer.cpp index d84ed388c9c44..7aae1dadaf873 100644 --- a/src/coreclr/vm/multicorejitplayer.cpp +++ b/src/coreclr/vm/multicorejitplayer.cpp @@ -396,11 +396,6 @@ bool MulticoreJitManager::ModuleHasNoCode(Module * pModule) { LIMITED_METHOD_CONTRACT; - if (pModule->IsResource()) - { - return true; - } - IMDInternalImport * pImport = pModule->GetMDImport(); if (pImport != NULL) @@ -434,15 +429,15 @@ bool MulticoreJitManager::IsSupportedModule(Module * pModule, bool fMethodJit) return false; } - PEFile * pFile = pModule->GetFile(); + PEAssembly * pPEAssembly = pModule->GetPEAssembly(); // dynamic module. - if (pFile->IsDynamic()) // Ignore dynamic modules + if (pPEAssembly->IsDynamic()) // Ignore dynamic modules { return false; } - if (pFile->GetPath().IsEmpty()) // Ignore in-memory modules + if (pPEAssembly->GetPath().IsEmpty()) // Ignore in-memory modules { return false; } diff --git a/src/coreclr/vm/nativeimage.cpp b/src/coreclr/vm/nativeimage.cpp index 13a39a2d4666e..b081091f1b94e 100644 --- a/src/coreclr/vm/nativeimage.cpp +++ b/src/coreclr/vm/nativeimage.cpp @@ -146,8 +146,9 @@ NativeImage *NativeImage::Open( BundleFileLocation bundleFileLocation = Bundle::ProbeAppBundle(fullPath, /*pathIsBundleRelative */ true); if (bundleFileLocation.IsValid()) { - PEImageHolder pImage = PEImage::OpenImage(fullPath, MDInternalImport_NoCache, bundleFileLocation); - peLoadedImage = pImage->GetLayout(PEImageLayout::LAYOUT_MAPPED, PEImage::LAYOUT_CREATEIFNEEDED); + PEImageHolder pImage = PEImage::OpenImage(fullPath, MDInternalImport_Default, bundleFileLocation); + peLoadedImage = pImage->GetOrCreateLayout(PEImageLayout::LAYOUT_MAPPED); + peLoadedImage.SuppressRelease(); } if (peLoadedImage.IsNull()) diff --git a/src/coreclr/vm/nativeimage.h b/src/coreclr/vm/nativeimage.h index bf3f7b6272edf..4774365197f2b 100644 --- a/src/coreclr/vm/nativeimage.h +++ b/src/coreclr/vm/nativeimage.h @@ -57,7 +57,7 @@ class NativeImageIndexTraits : public NoRemoveSHashTraitsGetManifestFile(); + PEAssembly *pManifestFile = pAssembly->GetManifestFile(); PTR_AssemblyBinder pBinder = pManifestFile->GetAssemblyBinder(); //Step 0: Check if the assembly was bound using TPA. diff --git a/src/coreclr/vm/pefile.cpp b/src/coreclr/vm/peassembly.cpp similarity index 67% rename from src/coreclr/vm/pefile.cpp rename to src/coreclr/vm/peassembly.cpp index 8e7d70732a27f..956512a3dbe06 100644 --- a/src/coreclr/vm/pefile.cpp +++ b/src/coreclr/vm/peassembly.cpp @@ -1,14 +1,14 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // -------------------------------------------------------------------------------- -// PEFile.cpp +// PEAssembly.cpp // // -------------------------------------------------------------------------------- #include "common.h" -#include "pefile.h" +#include "peassembly.h" #include "eecontract.h" #include "eeconfig.h" #include "eventtrace.h" @@ -28,127 +28,19 @@ #ifndef DACCESS_COMPILE -// ================================================================================ -// PEFile class - this is an abstract base class for PEAssembly -// ================================================================================ - -PEFile::PEFile(PEImage *identity) : -#if _DEBUG - m_pDebugName(NULL), -#endif - m_identity(NULL), - m_openedILimage(NULL), - m_MDImportIsRW_Debugger_Use_Only(FALSE), - m_bHasPersistentMDImport(FALSE), - m_pMDImport(NULL), - m_pImporter(NULL), - m_pEmitter(NULL), - m_pMetadataLock(::new SimpleRWLock(PREEMPTIVE, LOCK_TYPE_DEFAULT)), - m_refCount(1), - m_flags(0), - m_pHostAssembly(nullptr), - m_pFallbackBinder(nullptr) -{ - CONTRACTL - { - CONSTRUCTOR_CHECK; - THROWS; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - - if (identity) - { - identity->AddRef(); - m_identity = identity; - - if(identity->IsOpened()) - { - //already opened, prepopulate - identity->AddRef(); - m_openedILimage = identity; - } - } - - -} - - - -PEFile::~PEFile() -{ - CONTRACTL - { - DESTRUCTOR_CHECK; - NOTHROW; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - - ReleaseMetadataInterfaces(TRUE); - - - if (m_openedILimage != NULL) - m_openedILimage->Release(); - if (m_identity != NULL) - m_identity->Release(); - if (m_pMetadataLock) - delete m_pMetadataLock; - - if (m_pHostAssembly != NULL) - { - m_pHostAssembly->Release(); - } -} - -/* static */ -PEFile *PEFile::Open(PEImage *image) -{ - CONTRACT(PEFile *) - { - PRECONDITION(image != NULL); - PRECONDITION(image->CheckFormat()); - POSTCONDITION(RETVAL != NULL); - POSTCONDITION(!RETVAL->IsAssembly()); - THROWS; - GC_TRIGGERS; - MODE_ANY; - INJECT_FAULT(COMPlusThrowOM();); - } - CONTRACT_END; - - PEFile *pFile = new PEFile(image); - - if (image->HasNTHeaders() && image->HasCorHeader()) - pFile->OpenMDImport_Unsafe(); //no one else can see the object yet - -#if _DEBUG - pFile->m_debugName = image->GetPath(); - pFile->m_debugName.Normalize(); - pFile->m_pDebugName = pFile->m_debugName; -#endif - - RETURN pFile; -} - //----------------------------------------------------------------------------------------------------- // Catch attempts to load x64 assemblies on x86, etc. //----------------------------------------------------------------------------------------------------- -static void ValidatePEFileMachineType(PEFile *peFile) +static void ValidatePEFileMachineType(PEAssembly *pPEAssembly) { STANDARD_VM_CONTRACT; - if (peFile->IsDynamic()) + if (pPEAssembly->IsDynamic()) return; // PEFiles for ReflectionEmit assemblies don't cache the machine type. - if (peFile->IsResource()) - return; // PEFiles for resource assemblies don't cache the machine type. - DWORD peKind; DWORD actualMachineType; - peFile->GetPEKindAndMachine(&peKind, &actualMachineType); + pPEAssembly->GetPEKindAndMachine(&peKind, &actualMachineType); if (actualMachineType == IMAGE_FILE_MACHINE_I386 && ((peKind & (peILonly | pe32BitRequired)) == peILonly)) return; // Image is marked CPU-agnostic. @@ -167,10 +59,7 @@ static void ValidatePEFileMachineType(PEFile *peFile) // Image has required machine that doesn't match the CLR. StackSString name; - if (peFile->IsAssembly()) - ((PEAssembly*)peFile)->GetDisplayName(name); - else - name = StackSString(SString::Utf8, peFile->GetSimpleName()); + pPEAssembly->GetDisplayName(name); COMPlusThrow(kBadImageFormatException, IDS_CLASSLOAD_WRONGCPU, name.GetUnicode()); } @@ -178,12 +67,12 @@ static void ValidatePEFileMachineType(PEFile *peFile) return; // If we got here, all is good. } -void PEFile::LoadLibrary(BOOL allowNativeSkip/*=TRUE*/) // if allowNativeSkip==FALSE force IL image load +void PEAssembly::EnsureLoaded() { CONTRACT_VOID { INSTANCE_CHECK; - POSTCONDITION(CheckLoaded()); + POSTCONDITION(IsLoaded()); STANDARD_VM_CHECK; } CONTRACT_END; @@ -191,142 +80,64 @@ void PEFile::LoadLibrary(BOOL allowNativeSkip/*=TRUE*/) // if allowNativeSkip==F // Catch attempts to load x64 assemblies on x86, etc. ValidatePEFileMachineType(this); - // See if we've already loaded it. - if (CheckLoaded(allowNativeSkip)) + // See if we do not have anything to load or have already loaded it. + if (IsLoaded()) { RETURN; } // Note that we may be racing other threads here, in the case of domain neutral files - // Resource images are always flat. - if (IsResource()) - { - GetILimage()->LoadNoMetaData(); - RETURN; - } - #if !defined(TARGET_64BIT) - if (!GetILimage()->Has32BitNTHeaders()) + if (!GetPEImage()->Has32BitNTHeaders()) { // Tried to load 64-bit assembly on 32-bit platform. EEFileLoadException::Throw(this, COR_E_BADIMAGEFORMAT, NULL); } #endif - // We need contents now - EnsureImageOpened(); - // Since we couldn't call LoadLibrary, we must be an IL only image // or the image may still contain unfixed up stuff - if (!GetILimage()->IsILOnly()) + if (!GetPEImage()->IsILOnly()) { - if (!GetILimage()->HasV1Metadata()) + if (!GetPEImage()->HasV1Metadata()) ThrowHR(COR_E_FIXUPSINEXE); // @todo: better error } - if (GetILimage()->IsFile()) + if (GetPEImage()->IsFile()) { #ifdef TARGET_UNIX - bool loadILImage = GetILimage()->IsILOnly(); + bool loadILImage = GetPEImage()->IsILOnly(); #else // TARGET_UNIX - bool loadILImage = GetILimage()->IsILOnly() && GetILimage()->IsInBundle(); + bool loadILImage = GetPEImage()->IsILOnly() && GetPEImage()->IsInBundle(); #endif // TARGET_UNIX if (loadILImage) { - GetILimage()->Load(); + GetPEImage()->Load(); } else { - GetILimage()->LoadFromMapped(); + GetPEImage()->LoadFromMapped(); } } else { - GetILimage()->LoadNoFile(); - } - - RETURN; -} - -void PEFile::SetLoadedHMODULE(HMODULE hMod) -{ - CONTRACT_VOID - { - INSTANCE_CHECK; - PRECONDITION(CheckPointer(hMod)); - POSTCONDITION(CheckLoaded()); - THROWS; - GC_TRIGGERS; - MODE_ANY; - INJECT_FAULT(COMPlusThrowOM();); + GetPEImage()->LoadNoFile(); } - CONTRACT_END; - - // See if the image is an internal PEImage. - GetILimage()->SetLoadedHMODULE(hMod); RETURN; } -/* static */ -void PEFile::DefineEmitScope( - GUID iid, - void **ppEmit) -{ - CONTRACT_VOID - { - PRECONDITION(CheckPointer(ppEmit)); - POSTCONDITION(CheckPointer(*ppEmit)); - THROWS; - GC_TRIGGERS; - MODE_ANY; - INJECT_FAULT(COMPlusThrowOM();); - } - CONTRACT_END; - - SafeComHolder pDispenser; - - // Get the Dispenser interface. - MetaDataGetDispenser( - CLSID_CorMetaDataDispenser, - IID_IMetaDataDispenserEx, - (void **)&pDispenser); - if (pDispenser == NULL) - { - ThrowOutOfMemory(); - } - - // Set the option on the dispenser turn on duplicate check for TypeDef and moduleRef - VARIANT varOption; - V_VT(&varOption) = VT_UI4; - V_I4(&varOption) = MDDupDefault | MDDupTypeDef | MDDupModuleRef | MDDupExportedType | MDDupAssemblyRef | MDDupPermission | MDDupFile; - IfFailThrow(pDispenser->SetOption(MetaDataCheckDuplicatesFor, &varOption)); - - // Set minimal MetaData size - V_VT(&varOption) = VT_UI4; - V_I4(&varOption) = MDInitialSizeMinimal; - IfFailThrow(pDispenser->SetOption(MetaDataInitialSize, &varOption)); - - // turn on the thread safety! - V_I4(&varOption) = MDThreadSafetyOn; - IfFailThrow(pDispenser->SetOption(MetaDataThreadSafetyOptions, &varOption)); - - IfFailThrow(pDispenser->DefineScope(CLSID_CorMetaDataRuntime, 0, iid, (IUnknown **)ppEmit)); - - RETURN; -} // PEFile::DefineEmitScope - // ------------------------------------------------------------ // Identity // ------------------------------------------------------------ -BOOL PEFile::Equals(PEFile *pFile) +BOOL PEAssembly::Equals(PEAssembly *pPEAssembly) { CONTRACTL { INSTANCE_CHECK; - PRECONDITION(CheckPointer(pFile)); + PRECONDITION(CheckPointer(pPEAssembly)); GC_NOTRIGGER; NOTHROW; CANNOT_TAKE_LOCK; @@ -335,36 +146,31 @@ BOOL PEFile::Equals(PEFile *pFile) CONTRACTL_END; // Same object is equal - if (pFile == this) + if (pPEAssembly == this) return TRUE; // Different host assemblies cannot be equal unless they are associated with the same host binder // It's ok if only one has a host binder because multiple threads can race to load the same assembly // and that may cause temporary candidate PEAssembly objects that never get bound to a host assembly // because another thread beats it; the losing thread will pick up the PEAssembly in the cache. - if (pFile->HasHostAssembly() && this->HasHostAssembly()) + if (pPEAssembly->HasHostAssembly() && this->HasHostAssembly()) { - AssemblyBinder* fileBinder = pFile->GetHostAssembly()->GetBinder(); + AssemblyBinder* otherBinder = pPEAssembly->GetHostAssembly()->GetBinder(); AssemblyBinder* thisBinder = this->GetHostAssembly()->GetBinder(); - if (fileBinder != thisBinder || fileBinder == NULL) + if (otherBinder != thisBinder || otherBinder == NULL) return FALSE; } - // Same identity is equal - if (m_identity != NULL && pFile->m_identity != NULL - && m_identity->Equals(pFile->m_identity)) - return TRUE; - // Same image is equal - if (m_openedILimage != NULL && pFile->m_openedILimage != NULL - && m_openedILimage->Equals(pFile->m_openedILimage)) + if (m_PEImage != NULL && pPEAssembly->m_PEImage != NULL + && m_PEImage->Equals(pPEAssembly->m_PEImage)) return TRUE; return FALSE; } -BOOL PEFile::Equals(PEImage *pImage) +BOOL PEAssembly::Equals(PEImage *pImage) { CONTRACTL { @@ -376,21 +182,15 @@ BOOL PEFile::Equals(PEImage *pImage) } CONTRACTL_END; - // Same object is equal - if (pImage == m_identity || pImage == m_openedILimage) + // Same image ==> equal + if (pImage == m_PEImage) return TRUE; - // Same identity is equal - if (m_identity != NULL - && m_identity->Equals(pImage)) + // Equal image ==> equal + if (m_PEImage != NULL + && m_PEImage->Equals(pImage)) return TRUE; - // Same image is equal - if (m_openedILimage != NULL - && m_openedILimage->Equals(pImage)) - return TRUE; - - return FALSE; } @@ -398,7 +198,7 @@ BOOL PEFile::Equals(PEImage *pImage) // Descriptive strings // ------------------------------------------------------------ -void PEFile::GetCodeBaseOrName(SString &result) +void PEAssembly::GetPathOrCodeBase(SString &result) { CONTRACTL { @@ -410,47 +210,21 @@ void PEFile::GetCodeBaseOrName(SString &result) } CONTRACTL_END; - if (m_identity != NULL && !m_identity->GetPath().IsEmpty()) - { - result.Set(m_identity->GetPath()); - } - else if (IsAssembly()) + if (m_PEImage != NULL && !m_PEImage->GetPath().IsEmpty()) { - ((PEAssembly*)this)->GetCodeBase(result); + result.Set(m_PEImage->GetPath()); } else - result.SetUTF8(GetSimpleName()); -} - - -// ------------------------------------------------------------ -// Checks -// ------------------------------------------------------------ - - - -CHECK PEFile::CheckLoaded(BOOL bAllowNativeSkip/*=TRUE*/) -{ - CONTRACT_CHECK { - INSTANCE_CHECK; - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; + GetCodeBase(result); } - CONTRACT_CHECK_END; - - CHECK(IsLoaded(bAllowNativeSkip)); - - CHECK_OK; } - // ------------------------------------------------------------ // Metadata access // ------------------------------------------------------------ -PTR_CVOID PEFile::GetMetadata(COUNT_T *pSize) +PTR_CVOID PEAssembly::GetMetadata(COUNT_T *pSize) { CONTRACT(PTR_CVOID) { @@ -465,8 +239,8 @@ PTR_CVOID PEFile::GetMetadata(COUNT_T *pSize) CONTRACT_END; if (IsDynamic() - || !GetILimage()->HasNTHeaders() - || !GetILimage()->HasCorHeader()) + || !GetPEImage()->HasNTHeaders() + || !GetPEImage()->HasCorHeader()) { if (pSize != NULL) *pSize = 0; @@ -474,12 +248,12 @@ PTR_CVOID PEFile::GetMetadata(COUNT_T *pSize) } else { - RETURN GetILimage()->GetMetadata(pSize); + RETURN GetPEImage()->GetMetadata(pSize); } } #endif // #ifndef DACCESS_COMPILE -PTR_CVOID PEFile::GetLoadedMetadata(COUNT_T *pSize) +PTR_CVOID PEAssembly::GetLoadedMetadata(COUNT_T *pSize) { CONTRACT(PTR_CVOID) { @@ -493,9 +267,9 @@ PTR_CVOID PEFile::GetLoadedMetadata(COUNT_T *pSize) } CONTRACT_END; - if (!HasLoadedIL() - || !GetLoadedIL()->HasNTHeaders() - || !GetLoadedIL()->HasCorHeader()) + if (!HasLoadedPEImage() + || !GetLoadedLayout()->HasNTHeaders() + || !GetLoadedLayout()->HasCorHeader()) { if (pSize != NULL) *pSize = 0; @@ -503,20 +277,19 @@ PTR_CVOID PEFile::GetLoadedMetadata(COUNT_T *pSize) } else { - RETURN GetLoadedIL()->GetMetadata(pSize); + RETURN GetLoadedLayout()->GetMetadata(pSize); } } -TADDR PEFile::GetIL(RVA il) +TADDR PEAssembly::GetIL(RVA il) { CONTRACT(TADDR) { INSTANCE_CHECK; PRECONDITION(il != 0); PRECONDITION(!IsDynamic()); - PRECONDITION(!IsResource()); #ifndef DACCESS_COMPILE - PRECONDITION(CheckLoaded()); + PRECONDITION(HasLoadedPEImage()); #endif POSTCONDITION(RETVAL != NULL); THROWS; @@ -527,8 +300,7 @@ TADDR PEFile::GetIL(RVA il) CONTRACT_END; PEImageLayout *image = NULL; - - image = GetLoadedIL(); + image = GetLoadedLayout(); #ifndef DACCESS_COMPILE // Verify that the IL blob is valid before giving it out @@ -541,7 +313,7 @@ TADDR PEFile::GetIL(RVA il) #ifndef DACCESS_COMPILE -void PEFile::OpenImporter() +void PEAssembly::OpenImporter() { CONTRACTL { @@ -557,7 +329,7 @@ void PEFile::OpenImporter() ConvertMDInternalToReadWrite(); IMetaDataImport2 *pIMDImport = NULL; - IfFailThrow(GetMetaDataPublicInterfaceFromInternal((void*)GetPersistentMDImport(), + IfFailThrow(GetMetaDataPublicInterfaceFromInternal((void*)GetMDImport(), IID_IMetaDataImport2, (void **)&pIMDImport)); @@ -566,7 +338,7 @@ void PEFile::OpenImporter() pIMDImport->Release(); } -void PEFile::ConvertMDInternalToReadWrite() +void PEAssembly::ConvertMDInternalToReadWrite() { CONTRACTL { @@ -617,7 +389,6 @@ void PEFile::ConvertMDInternalToReadWrite() // Swap the pointers in a thread safe manner. If the contents of *ppImport // equals pOld then no other thread got here first, and the old contents are // replaced with pNew. The old contents are returned. - _ASSERTE(m_bHasPersistentMDImport); if (FastInterlockCompareExchangePointer(&m_pMDImport, pNew, pOld) == pOld) { //if the debugger queries, it will now see that we have RW metadata @@ -634,7 +405,7 @@ void PEFile::ConvertMDInternalToReadWrite() } } -void PEFile::OpenMDImport_Unsafe() +void PEAssembly::OpenMDImport() { CONTRACTL { @@ -649,23 +420,21 @@ void PEFile::OpenMDImport_Unsafe() if (m_pMDImport != NULL) return; if (!IsDynamic() - && GetILimage()->HasNTHeaders() - && GetILimage()->HasCorHeader()) + && GetPEImage()->HasNTHeaders() + && GetPEImage()->HasCorHeader()) { - m_pMDImport=GetILimage()->GetMDImport(); + m_pMDImport=GetPEImage()->GetMDImport(); } else { ThrowHR(COR_E_BADIMAGEFORMAT); } - m_bHasPersistentMDImport=TRUE; - _ASSERTE(m_pMDImport); m_pMDImport->AddRef(); } -void PEFile::OpenEmitter() +void PEAssembly::OpenEmitter() { CONTRACTL { @@ -681,7 +450,7 @@ void PEFile::OpenEmitter() ConvertMDInternalToReadWrite(); IMetaDataEmit *pIMDEmit = NULL; - IfFailThrow(GetMetaDataPublicInterfaceFromInternal((void*)GetPersistentMDImport(), + IfFailThrow(GetMetaDataPublicInterfaceFromInternal((void*)GetMDImport(), IID_IMetaDataEmit, (void **)&pIMDEmit)); @@ -690,39 +459,6 @@ void PEFile::OpenEmitter() pIMDEmit->Release(); } - -void PEFile::ReleaseMetadataInterfaces(BOOL bDestructor) -{ - CONTRACTL - { - INSTANCE_CHECK; - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(bDestructor||m_pMetadataLock->IsWriterLock()); - } - CONTRACTL_END; - _ASSERTE(bDestructor || !m_bHasPersistentMDImport); - - if (m_pImporter != NULL) - { - m_pImporter->Release(); - m_pImporter = NULL; - } - if (m_pEmitter != NULL) - { - m_pEmitter->Release(); - m_pEmitter = NULL; - } - - if (m_pMDImport != NULL) - { - m_pMDImport->Release(); - m_pMDImport=NULL; - } -} - - // ------------------------------------------------------------ // PE file access // ------------------------------------------------------------ @@ -740,7 +476,7 @@ void PEFile::ReleaseMetadataInterfaces(BOOL bDestructor) // Resource access // ------------------------------------------------------------ -void PEFile::GetEmbeddedResource(DWORD dwOffset, DWORD *cbResource, PBYTE *pbInMemoryResource) +void PEAssembly::GetEmbeddedResource(DWORD dwOffset, DWORD *cbResource, PBYTE *pbInMemoryResource) { CONTRACTL { @@ -756,10 +492,9 @@ void PEFile::GetEmbeddedResource(DWORD dwOffset, DWORD *cbResource, PBYTE *pbInM // m_loadedImage is probably preferable, but this may be called by security // before the image is loaded. - EnsureImageOpened(); - PEImage* image = GetILimage(); + PEImage* image = GetPEImage(); - PEImageLayoutHolder theImage(image->GetLayout(PEImageLayout::LAYOUT_ANY,PEImage::LAYOUT_CREATEIFNEEDED)); + PEImageLayout* theImage = image->GetOrCreateLayout(PEImageLayout::LAYOUT_ANY); if (!theImage->CheckResource(dwOffset)) ThrowHR(COR_E_BADIMAGEFORMAT); @@ -774,10 +509,7 @@ void PEFile::GetEmbeddedResource(DWORD dwOffset, DWORD *cbResource, PBYTE *pbInM // File loading // ------------------------------------------------------------ -PEAssembly * -PEFile::LoadAssembly( - mdAssemblyRef kAssemblyRef, - IMDInternalImport * pImport) +PEAssembly* PEAssembly::LoadAssembly(mdAssemblyRef kAssemblyRef) { CONTRACT(PEAssembly *) { @@ -790,9 +522,7 @@ PEFile::LoadAssembly( } CONTRACT_END; - if (pImport == NULL) - pImport = GetPersistentMDImport(); - + IMDInternalImport* pImport = GetMDImport(); if (((TypeFromToken(kAssemblyRef) != mdtAssembly) && (TypeFromToken(kAssemblyRef) != mdtAssemblyRef)) || (!pImport->IsValidToken(kAssemblyRef))) @@ -802,16 +532,16 @@ PEFile::LoadAssembly( AssemblySpec spec; - spec.InitializeSpec(kAssemblyRef, pImport, GetAppDomain()->FindAssembly(GetAssembly())); + spec.InitializeSpec(kAssemblyRef, pImport, GetAppDomain()->FindAssembly(this)); RETURN GetAppDomain()->BindAssemblySpec(&spec, TRUE); } -BOOL PEFile::GetResource(LPCSTR szName, DWORD *cbResource, - PBYTE *pbInMemoryResource, DomainAssembly** pAssemblyRef, - LPCSTR *szFileName, DWORD *dwLocation, - BOOL fSkipRaiseResolveEvent, DomainAssembly* pDomainAssembly, AppDomain* pAppDomain) +BOOL PEAssembly::GetResource(LPCSTR szName, DWORD *cbResource, + PBYTE *pbInMemoryResource, DomainAssembly** pAssemblyRef, + LPCSTR *szFileName, DWORD *dwLocation, + BOOL fSkipRaiseResolveEvent, DomainAssembly* pDomainAssembly, AppDomain* pAppDomain) { CONTRACTL { @@ -829,11 +559,11 @@ BOOL PEFile::GetResource(LPCSTR szName, DWORD *cbResource, DWORD dwOffset; mdManifestResource mdResource; Assembly* pAssembly = NULL; - PEFile* pPEFile = NULL; - ReleaseHolder pImport (GetMDImportWithRef()); + PEAssembly* pPEAssembly = NULL; + IMDInternalImport* pImport = GetMDImport(); if (SUCCEEDED(pImport->FindManifestResourceByName(szName, &mdResource))) { - pPEFile = this; + pPEAssembly = this; IfFailThrow(pImport->GetManifestResourceProps( mdResource, NULL, //&szName, @@ -846,13 +576,13 @@ BOOL PEFile::GetResource(LPCSTR szName, DWORD *cbResource, if (fSkipRaiseResolveEvent || pAppDomain == NULL) return FALSE; - DomainAssembly* pParentAssembly = GetAppDomain()->FindAssembly(GetAssembly()); + DomainAssembly* pParentAssembly = GetAppDomain()->FindAssembly(this); pAssembly = pAppDomain->RaiseResourceResolveEvent(pParentAssembly, szName); if (pAssembly == NULL) return FALSE; pDomainAssembly = pAssembly->GetDomainAssembly(); - pPEFile = pDomainAssembly->GetFile(); + pPEAssembly = pDomainAssembly->GetPEAssembly(); if (FAILED(pAssembly->GetManifestImport()->FindManifestResourceByName( szName, @@ -868,7 +598,7 @@ BOOL PEFile::GetResource(LPCSTR szName, DWORD *cbResource, *dwLocation = *dwLocation | 2; // ResourceLocation.containedInAnotherAssembly } - IfFailThrow(pPEFile->GetPersistentMDImport()->GetManifestResourceProps( + IfFailThrow(pPEAssembly->GetMDImport()->GetManifestResourceProps( mdResource, NULL, //&szName, &mdLinkRef, @@ -884,7 +614,7 @@ BOOL PEFile::GetResource(LPCSTR szName, DWORD *cbResource, return FALSE; AssemblySpec spec; - spec.InitializeSpec(mdLinkRef, GetPersistentMDImport(), pDomainAssembly); + spec.InitializeSpec(mdLinkRef, GetMDImport(), pDomainAssembly); pDomainAssembly = spec.LoadDomainAssembly(FILE_LOADED); if (dwLocation) { @@ -915,7 +645,7 @@ BOOL PEFile::GetResource(LPCSTR szName, DWORD *cbResource, return TRUE; } - pPEFile->GetEmbeddedResource(dwOffset, cbResource, pbInMemoryResource); + pPEAssembly->GetEmbeddedResource(dwOffset, cbResource, pbInMemoryResource); return TRUE; } @@ -926,24 +656,23 @@ BOOL PEFile::GetResource(LPCSTR szName, DWORD *cbResource, } } -void PEFile::GetPEKindAndMachine(DWORD* pdwKind, DWORD* pdwMachine) +void PEAssembly::GetPEKindAndMachine(DWORD* pdwKind, DWORD* pdwMachine) { WRAPPER_NO_CONTRACT; - if (IsResource() || IsDynamic()) + _ASSERTE(pdwKind != NULL && pdwMachine != NULL); + if (IsDynamic()) { - if (pdwKind) - *pdwKind = 0; - if (pdwMachine) - *pdwMachine = 0; + *pdwKind = 0; + *pdwMachine = 0; return; } - GetILimage()->GetPEKindAndMachine(pdwKind, pdwMachine); + GetPEImage()->GetPEKindAndMachine(pdwKind, pdwMachine); return; } -ULONG PEFile::GetILImageTimeDateStamp() +ULONG PEAssembly::GetPEImageTimeDateStamp() { CONTRACTL { @@ -953,62 +682,63 @@ ULONG PEFile::GetILImageTimeDateStamp() } CONTRACTL_END; - return GetLoadedIL()->GetTimeDateStamp(); -} - - -// ================================================================================ -// PEAssembly class - a PEFile which represents an assembly -// ================================================================================ - -// Statics initialization. -/* static */ -void PEAssembly::Attach() -{ - STANDARD_VM_CONTRACT; + return GetLoadedLayout()->GetTimeDateStamp(); } #ifndef DACCESS_COMPILE + PEAssembly::PEAssembly( BINDER_SPACE::Assembly* pBindResultInfo, IMetaDataEmit* pEmit, - PEFile *creator, - BOOL system, - PEImage * pPEImageIL /*= NULL*/, + PEAssembly *creator, + BOOL isSystem, + PEImage * pPEImage /*= NULL*/, BINDER_SPACE::Assembly * pHostAssembly /*= NULL*/) - - : PEFile(pBindResultInfo ? pBindResultInfo->GetPEImage() - : pPEImageIL), - m_creator(clr::SafeAddRef(creator)) { CONTRACTL { CONSTRUCTOR_CHECK; PRECONDITION(CheckPointer(pEmit, NULL_OK)); PRECONDITION(CheckPointer(creator, NULL_OK)); - PRECONDITION(pBindResultInfo == NULL || pPEImageIL == NULL); + PRECONDITION(pBindResultInfo == NULL || pPEImage == NULL); STANDARD_VM_CHECK; } CONTRACTL_END; - m_flags |= PEFILE_ASSEMBLY; - if (system) - m_flags |= PEFILE_SYSTEM; + m_creator = clr::SafeAddRef(creator); +#if _DEBUG + m_pDebugName = NULL; +#endif + m_PEImage = NULL; + m_MDImportIsRW_Debugger_Use_Only = FALSE; + m_pMDImport = NULL; + m_pImporter = NULL; + m_pEmitter = NULL; + m_refCount = 1; + m_isSystem = isSystem; + m_pHostAssembly = nullptr; + m_pFallbackBinder = nullptr; - // We require a mapping for the file. - EnsureImageOpened(); + pPEImage = pBindResultInfo ? pBindResultInfo->GetPEImage() : pPEImage; + if (pPEImage) + { + _ASSERTE(pPEImage->CheckUniqueInstance()); + pPEImage->AddRef(); + + // We require a mapping for the file. + pPEImage->GetOrCreateLayout(PEImageLayout::LAYOUT_ANY); + m_PEImage = pPEImage; + } // Open metadata eagerly to minimize failure windows if (pEmit == NULL) - OpenMDImport_Unsafe(); //constructor, cannot race with anything + OpenMDImport(); //constructor, cannot race with anything else { - _ASSERTE(!m_bHasPersistentMDImport); IfFailThrow(GetMetaDataInternalInterfaceFromPublic(pEmit, IID_IMDInternalImport, (void **)&m_pMDImport)); m_pEmitter = pEmit; pEmit->AddRef(); - m_bHasPersistentMDImport=TRUE; m_MDImportIsRW_Debugger_Use_Only = TRUE; } @@ -1040,7 +770,7 @@ PEAssembly::PEAssembly( } #if _DEBUG - GetCodeBaseOrName(m_debugName); + GetPathOrCodeBase(m_debugName); m_debugName.Normalize(); m_pDebugName = m_debugName; #endif @@ -1058,7 +788,7 @@ PEAssembly *PEAssembly::Open( PEAssembly * pPEAssembly = new PEAssembly( nullptr, // BindResult nullptr, // IMetaDataEmit - pParent, // PEFile creator + pParent, // PEAssembly creator FALSE, // isSystem pPEImageIL, pHostAssembly); @@ -1082,6 +812,29 @@ PEAssembly::~PEAssembly() if (m_creator != NULL) m_creator->Release(); + if (m_pImporter != NULL) + { + m_pImporter->Release(); + m_pImporter = NULL; + } + + if (m_pEmitter != NULL) + { + m_pEmitter->Release(); + m_pEmitter = NULL; + } + + if (m_pMDImport != NULL) + { + m_pMDImport->Release(); + m_pMDImport = NULL; + } + + if (m_PEImage != NULL) + m_PEImage->Release(); + + if (m_pHostAssembly != NULL) + m_pHostAssembly->Release(); } /* static */ @@ -1126,12 +879,9 @@ PEAssembly *PEAssembly::DoOpenSystem() RETURN new PEAssembly(pBoundAssembly, NULL, NULL, TRUE); } -PEAssembly* PEAssembly::Open(BINDER_SPACE::Assembly* pBindResult, - BOOL isSystem) +PEAssembly* PEAssembly::Open(BINDER_SPACE::Assembly* pBindResult) { - - return new PEAssembly(pBindResult,NULL,NULL,isSystem); - + return new PEAssembly(pBindResult,NULL,NULL, /*isSystem*/ false); }; /* static */ @@ -1151,15 +901,12 @@ PEAssembly *PEAssembly::Create(PEAssembly *pParentAssembly, // we have.) SafeComHolder pEmit; pAssemblyEmit->QueryInterface(IID_IMetaDataEmit, (void **)&pEmit); - PEAssemblyHolder pFile(new PEAssembly(NULL, pEmit, pParentAssembly, FALSE)); - RETURN pFile.Extract(); + RETURN new PEAssembly(NULL, pEmit, pParentAssembly, FALSE); } - #endif // #ifndef DACCESS_COMPILE - #ifndef DACCESS_COMPILE // ------------------------------------------------------------ @@ -1179,23 +926,23 @@ const SString &PEAssembly::GetEffectivePath() } CONTRACTL_END; - PEAssembly *pAssembly = this; + PEAssembly* pPEAssembly = this; - while (pAssembly->m_identity == NULL - || pAssembly->m_identity->GetPath().IsEmpty()) + while (pPEAssembly->m_PEImage == NULL + || pPEAssembly->m_PEImage->GetPath().IsEmpty()) { - if (pAssembly->m_creator) - pAssembly = pAssembly->m_creator->GetAssembly(); + if (pPEAssembly->m_creator) + pPEAssembly = pPEAssembly->m_creator; else // Unmanaged exe which loads byte[]/IStream assemblies return SString::Empty(); } - return pAssembly->m_identity->GetPath(); + return pPEAssembly->m_PEImage->GetPath(); } // Codebase is the fusion codebase or path for the assembly. It is in URL format. -// Note this may be obtained from the parent PEFile if we don't have a path or fusion +// Note this may be obtained from the parent PEAssembly if we don't have a path or fusion // assembly. // Returns false if the assembly was loaded from a bundle, true otherwise BOOL PEAssembly::GetCodeBase(SString &result) @@ -1210,13 +957,14 @@ BOOL PEAssembly::GetCodeBase(SString &result) } CONTRACTL_END; - auto ilImage = GetILimage(); - if (ilImage == nullptr || !ilImage->IsInBundle()) + PEImage* ilImage = GetPEImage(); + if (ilImage == NULL || !ilImage->IsInBundle()) { // All other cases use the file path. result.Set(GetEffectivePath()); if (!result.IsEmpty()) PathToUrl(result); + return TRUE; } else @@ -1322,7 +1070,7 @@ BOOL PEAssembly::FindLastPathSeparator(const SString &path, SString::Iterator &i // Metadata access // ------------------------------------------------------------ -HRESULT PEFile::GetVersion(USHORT *pMajor, USHORT *pMinor, USHORT *pBuild, USHORT *pRevision) +HRESULT PEAssembly::GetVersion(USHORT *pMajor, USHORT *pMinor, USHORT *pBuild, USHORT *pRevision) { CONTRACTL { @@ -1337,19 +1085,11 @@ HRESULT PEFile::GetVersion(USHORT *pMajor, USHORT *pMinor, USHORT *pBuild, USHOR } CONTRACTL_END; - AssemblyMetaDataInternal md; + _ASSERTE(GetMDImport()->IsValidToken(TokenFromRid(1, mdtAssembly))); + HRESULT hr = S_OK;; - if (m_bHasPersistentMDImport) - { - _ASSERTE(GetPersistentMDImport()->IsValidToken(TokenFromRid(1, mdtAssembly))); - IfFailRet(GetPersistentMDImport()->GetAssemblyProps(TokenFromRid(1, mdtAssembly), NULL, NULL, NULL, NULL, &md, NULL)); - } - else - { - ReleaseHolder pImport(GetMDImportWithRef()); - _ASSERTE(pImport->IsValidToken(TokenFromRid(1, mdtAssembly))); - IfFailRet(pImport->GetAssemblyProps(TokenFromRid(1, mdtAssembly), NULL, NULL, NULL, NULL, &md, NULL)); - } + AssemblyMetaDataInternal md; + IfFailRet(GetMDImport()->GetAssemblyProps(TokenFromRid(1, mdtAssembly), NULL, NULL, NULL, NULL, &md, NULL)); if (pMajor != NULL) *pMajor = md.usMajorVersion; @@ -1360,55 +1100,30 @@ HRESULT PEFile::GetVersion(USHORT *pMajor, USHORT *pMinor, USHORT *pBuild, USHOR if (pRevision != NULL) *pRevision = md.usRevisionNumber; - return hr; -} - - - -void PEFile::EnsureImageOpened() -{ - WRAPPER_NO_CONTRACT; - if (IsDynamic()) - return; - - GetILimage()->GetLayout(PEImageLayout::LAYOUT_ANY,PEImage::LAYOUT_CREATEIFNEEDED)->Release(); + return S_OK; } #endif // #ifndef DACCESS_COMPILE #ifdef DACCESS_COMPILE -void -PEFile::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) +void PEAssembly::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) { WRAPPER_NO_CONTRACT; SUPPORTS_DAC; - // sizeof(PEFile) == 0xb8 DAC_ENUM_VTHIS(); - EMEM_OUT(("MEM: %p PEFile\n", dac_cast(this))); + EMEM_OUT(("MEM: %p PEAssembly\n", dac_cast(this))); #ifdef _DEBUG // Not a big deal if it's NULL or fails. m_debugName.EnumMemoryRegions(flags); #endif - if (m_identity.IsValid()) + if (m_PEImage.IsValid()) { - m_identity->EnumMemoryRegions(flags); + m_PEImage->EnumMemoryRegions(flags); } - if (GetILimage().IsValid()) - { - GetILimage()->EnumMemoryRegions(flags); - } -} - -void -PEAssembly::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) -{ - WRAPPER_NO_CONTRACT; - - PEFile::EnumMemoryRegions(flags); if (m_creator.IsValid()) { @@ -1426,7 +1141,7 @@ PEAssembly::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) // It can return an empty if the name isn't available or the object isn't initialized // enough to get a name, but it mustn't crash. //------------------------------------------------------------------------------- -LPCWSTR PEFile::GetPathForErrorMessages() +LPCWSTR PEAssembly::GetPathForErrorMessages() { CONTRACTL { @@ -1439,7 +1154,7 @@ LPCWSTR PEFile::GetPathForErrorMessages() if (!IsDynamic()) { - return m_identity->GetPathForErrorMessages(); + return m_PEImage->GetPathForErrorMessages(); } else { @@ -1449,7 +1164,7 @@ LPCWSTR PEFile::GetPathForErrorMessages() #ifdef DACCESS_COMPILE -TADDR PEFile::GetMDInternalRWAddress() +TADDR PEAssembly::GetMDInternalRWAddress() { if (!m_MDImportIsRW_Debugger_Use_Only) return 0; @@ -1467,13 +1182,13 @@ TADDR PEFile::GetMDInternalRWAddress() // 3) ASSUMPTION: We are assuming that no pointer adjustment is required to convert between // IMDInternalImport*, IMDInternalImportENC* and MDInternalRW*. Ideally I was hoping to do this with a // static_cast<> but the compiler complains that the ENC<->RW is an unrelated conversion. - return (TADDR) m_pMDImport_UseAccessor; + return (TADDR)m_pMDImport_UseAccessor; } } #endif -// Returns the AssemblyBinder* instance associated with the PEFile -PTR_AssemblyBinder PEFile::GetAssemblyBinder() +// Returns the AssemblyBinder* instance associated with the PEAssembly +PTR_AssemblyBinder PEAssembly::GetAssemblyBinder() { LIMITED_METHOD_CONTRACT; diff --git a/src/coreclr/vm/pefile.h b/src/coreclr/vm/peassembly.h similarity index 57% rename from src/coreclr/vm/pefile.h rename to src/coreclr/vm/peassembly.h index 317c9c544228f..adc19df11bc9b 100644 --- a/src/coreclr/vm/pefile.h +++ b/src/coreclr/vm/peassembly.h @@ -1,14 +1,14 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // -------------------------------------------------------------------------------- -// PEFile.h +// PEAssembly.h // // -------------------------------------------------------------------------------- -#ifndef PEFILE_H_ -#define PEFILE_H_ +#ifndef PEASSEMBLY_H_ +#define PEASSEMBLY_H_ // -------------------------------------------------------------------------------- // Required headers @@ -41,7 +41,6 @@ class Module; class EditAndContinueModule; -class PEFile; class PEAssembly; class SimpleRWLock; @@ -52,23 +51,25 @@ typedef VPTR(PEAssembly) PTR_PEAssembly; // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- -// A PEFile is an input to the CLR loader. It is produced as a result of +// A PEAssembly is an input to the CLR loader. It is produced as a result of // binding, usually through fusion (although there are a few less common methods to // obtain one which do not go through fusion, e.g. IJW loads) // -// Although a PEFile is usually a disk based PE file (hence the name), it is not +// Although a PEAssembly is usually a disk based PE file, it is not // always the case. Thus it is a conscious decision to not export access to the PE // file directly; rather the specific information required should be provided via // individual query API. // -// There are multiple "flavors" of PEFiles: +// There are multiple "flavors" of PEAssemblies: // // 1. HMODULE - these PE Files are loaded in response to "spontaneous" OS callbacks. // These should only occur for .exe main modules and IJW dlls loaded via LoadLibrary // or static imports in umnanaged code. +// These get their PEImage loaded directly in PEImage::LoadImage(HMODULE hMod) // -// 2. Fusion loads - these are the most common case. A path is obtained from fusion and -// the result is loaded via PEImage. +// 2. Assemblies loaded directly or indirectly by the managed code - these are the most +// common case. A path is obtained from assembly binding and the result is loaded +// via PEImage: // a. Display name loads - these are metadata-based binds // b. Path loads - these are loaded from an explicit path // @@ -78,19 +79,14 @@ typedef VPTR(PEAssembly) PTR_PEAssembly; // for reflection-based modules. // // See also file:..\inc\corhdr.h#ManagedHeader for more on the format of managed images. -// See code:Module for more on modules // -------------------------------------------------------------------------------- -typedef VPTR(class PEFile) PTR_PEFile; - -typedef ReleaseHolder IMDInternalImportHolder; - -class PEFile +class PEAssembly final { // ------------------------------------------------------------ // SOS support // ------------------------------------------------------------ - VPTR_BASE_CONCRETE_VTABLE_CLASS(PEFile) + VPTR_BASE_CONCRETE_VTABLE_CLASS(PEAssembly) public: @@ -109,54 +105,15 @@ class PEFile CHECK Invariant(); #endif -private: - // ------------------------------------------------------------ - // Loader access API - // ------------------------------------------------------------ - - friend class DomainFile; - -public: - void LoadLibrary(BOOL allowNativeSkip = TRUE); - - -private: - // For use inside LoadLibrary callback - void SetLoadedHMODULE(HMODULE hMod); - - // DO NOT USE !!! this is to be removed when we move to new fusion binding API - friend class DomainAssembly; - - // Helper for creating metadata for CreateDynamic - friend class Assembly; - friend class COMDynamicWrite; - friend class AssemblyNative; - static void DefineEmitScope( - GUID iid, - void **ppEmit); - -protected: - IMDInternalImportHolder GetMDImport(); - -public: - // ------------------------------------------------------------ - // Generic PEFile - can be used to access metadata - // ------------------------------------------------------------ - - static PEFile *Open(PEImage *image); - // ------------------------------------------------------------ // Identity // ------------------------------------------------------------ #ifndef DACCESS_COMPILE - BOOL Equals(PEFile *pFile); + BOOL Equals(PEAssembly *pPEAssembly); BOOL Equals(PEImage *pImage); #endif // DACCESS_COMPILE - - void GetMVID(GUID *pMvid); - // ------------------------------------------------------------ // Descriptive strings // ------------------------------------------------------------ @@ -170,8 +127,22 @@ class PEFile const SString &GetModuleFileNameHint(); #endif // DACCESS_COMPILE + LPCWSTR GetPathForErrorMessages(); + + // This returns a non-empty path representing the source of the assembly; it may + // be the parent assembly for dynamic or memory assemblies + const SString& GetEffectivePath(); + + // Codebase is the fusion codebase or path for the assembly. It is in URL format. + // Note this may be obtained from the parent PEAssembly if we don't have a path or fusion + // assembly. + BOOL GetCodeBase(SString& result); + // Full name is the most descriptive name available (path, codebase, or name as appropriate) - void GetCodeBaseOrName(SString &result); + void GetPathOrCodeBase(SString& result); + + // Display name is the fusion binding name for an assembly + void GetDisplayName(SString& result, DWORD flags = 0); #ifdef LOGGING // This is useful for log messages @@ -182,68 +153,49 @@ class PEFile // Checks // ------------------------------------------------------------ - CHECK CheckLoaded(BOOL allowNativeSkip = TRUE); void ValidateForExecution(); BOOL IsMarkedAsNoPlatform(); - // ------------------------------------------------------------ // Classification // ------------------------------------------------------------ - BOOL IsAssembly() const; - PTR_PEAssembly AsAssembly(); BOOL IsSystem() const; BOOL IsDynamic() const; - BOOL IsResource() const; - BOOL IsIStream() const; - // Returns self (if assembly) or containing assembly (if module) - PEAssembly *GetAssembly() const; // ------------------------------------------------------------ // Metadata access // ------------------------------------------------------------ - BOOL HasMetadata(); - - IMDInternalImport *GetPersistentMDImport(); - IMDInternalImport *GetMDImportWithRef(); - void MakeMDImportPersistent() {m_bHasPersistentMDImport=TRUE;}; + IMDInternalImport *GetMDImport(); #ifndef DACCESS_COMPILE IMetaDataEmit *GetEmitter(); - IMetaDataAssemblyEmit *GetAssemblyEmitter(); IMetaDataImport2 *GetRWImporter(); - IMetaDataAssemblyImport *GetAssemblyImporter(); #else TADDR GetMDInternalRWAddress(); #endif // DACCESS_COMPILE + void ConvertMDInternalToReadWrite(); + + void GetMVID(GUID* pMvid); + ULONG GetHashAlgId(); + HRESULT GetVersion(USHORT* pMajor, USHORT* pMinor, USHORT* pBuild, USHORT* pRevision); + BOOL IsStrongNamed(); LPCUTF8 GetSimpleName(); HRESULT GetScopeName(LPCUTF8 * pszName); - BOOL IsStrongNameVerified(); - BOOL IsStrongNamed(); const void *GetPublicKey(DWORD *pcbPK); - ULONG GetHashAlgId(); - HRESULT GetVersion(USHORT *pMajor, USHORT *pMinor, USHORT *pBuild, USHORT *pRevision); LPCSTR GetLocale(); DWORD GetFlags(); - HRESULT GetFlagsNoTrigger(DWORD * pdwFlags); + // ------------------------------------------------------------ // PE file access // ------------------------------------------------------------ - BOOL IsIbcOptimized(); BOOL IsReadyToRun(); - WORD GetSubsystem(); - mdToken GetEntryPointToken( -#ifdef _DEBUG - BOOL bAssumeLoaded = FALSE -#endif //_DEBUG - ); - BOOL IsILOnly(); - BOOL IsDll(); + mdToken GetEntryPointToken(); + BOOL IsILOnly(); TADDR GetIL(RVA il); PTR_VOID GetRvaField(RVA field); @@ -266,15 +218,14 @@ class PEFile LPCSTR *szFileName, DWORD *dwLocation, BOOL fSkipRaiseResolveEvent, DomainAssembly* pDomainAssembly, AppDomain* pAppDomain); + #ifndef DACCESS_COMPILE PTR_CVOID GetMetadata(COUNT_T *pSize); #endif - PTR_CVOID GetLoadedMetadata(COUNT_T *pSize); + PTR_CVOID GetLoadedMetadata(COUNT_T *pSize); void GetPEKindAndMachine(DWORD* pdwKind, DWORD* pdwMachine); - - ULONG GetILImageTimeDateStamp(); - + ULONG GetPEImageTimeDateStamp(); // ------------------------------------------------------------ // Image memory access @@ -288,32 +239,54 @@ class PEFile // in the no-IL image case. // ------------------------------------------------------------ + BOOL HasPEImage() + { + LIMITED_METHOD_DAC_CONTRACT; + return m_PEImage != NULL; + } + + PEImage* GetPEImage() + { + LIMITED_METHOD_DAC_CONTRACT; + return m_PEImage; + } + + void EnsureLoaded(); + + BOOL HasLoadedPEImage() + { + LIMITED_METHOD_DAC_CONTRACT; + return HasPEImage() && GetPEImage()->HasLoadedLayout(); + } + + PTR_PEImageLayout GetLoadedLayout() + { + LIMITED_METHOD_CONTRACT; + SUPPORTS_DAC; + + _ASSERTE(HasPEImage()); + return GetPEImage()->GetLoadedLayout(); + }; + + BOOL IsLoaded() + { + return IsDynamic() || HasLoadedPEImage(); + } + + BOOL IsPtrInPEImage(PTR_CVOID data); + // For IJW purposes only - this asserts that we have an IJW image. HMODULE GetIJWBase(); // The debugger can tolerate a null value here for native only loading cases - PTR_VOID GetDebuggerContents(COUNT_T *pSize = NULL); + PTR_VOID GetDebuggerContents(COUNT_T* pSize = NULL); #ifndef DACCESS_COMPILE // Returns the IL image range; may force a LoadLibrary - const void *GetManagedFileContents(COUNT_T *pSize = NULL); + const void* GetManagedFileContents(COUNT_T* pSize = NULL); #endif // DACCESS_COMPILE - PTR_CVOID GetLoadedImageContents(COUNT_T *pSize = NULL); - - // ------------------------------------------------------------ - // Native image access - // ------------------------------------------------------------ - - // Does the loader support using a native image for this file? - // Some implementation restrictions prevent native images from being used - // in some cases. - PTR_PEImageLayout GetLoaded(); - PTR_PEImageLayout GetLoadedIL(); - IStream * GetPdbStream(); - void ClearPdbStream(); - BOOL IsLoaded(BOOL bAllowNativeSkip=TRUE) ; - BOOL IsPtrInILImage(PTR_CVOID data); + PTR_CVOID GetLoadedImageContents(COUNT_T* pSize = NULL); // ------------------------------------------------------------ // Resource access @@ -325,141 +298,18 @@ class PEFile // File loading // ------------------------------------------------------------ - PEAssembly * LoadAssembly( - mdAssemblyRef kAssemblyRef, - IMDInternalImport * pImport = NULL); - -protected: - // ------------------------------------------------------------ - // Internal constants - // ------------------------------------------------------------ - - enum - { - PEFILE_SYSTEM = 0x01, - PEFILE_ASSEMBLY = 0x02, - }; - - // ------------------------------------------------------------ - // Internal routines - // ------------------------------------------------------------ - -#ifndef DACCESS_COMPILE - PEFile(PEImage *image); - virtual ~PEFile(); -#else - virtual ~PEFile() {} -#endif - - void OpenMDImport(); - void OpenMDImport_Unsafe(); - void OpenImporter(); - void OpenEmitter(); - - void ReleaseMetadataInterfaces(BOOL bDestructor); - - - friend class Module; - -#ifndef DACCESS_COMPILE - void EnsureImageOpened(); -#endif // DACCESS_COMPILE - - friend class ClrDataAccess; + PEAssembly * LoadAssembly(mdAssemblyRef kAssemblyRef); // ------------------------------------------------------------ - // Instance fields + // Assembly Binder and host assembly (BINDER_SPACE::Assembly) // ------------------------------------------------------------ -#ifdef _DEBUG - LPCWSTR m_pDebugName; - SString m_debugName; -#endif - - // Identity image - PTR_PEImage m_identity; - // IL image, NULL if we didn't need to open the file - PTR_PEImage m_openedILimage; - // This flag is not updated atomically with m_pMDImport. Its fine for debugger usage - // but don't rely on it in the runtime. In runtime try QI'ing the m_pMDImport for - // IID_IMDInternalImportENC - BOOL m_MDImportIsRW_Debugger_Use_Only; - Volatile m_bHasPersistentMDImport; - -#ifndef DACCESS_COMPILE - IMDInternalImport *m_pMDImport; -#else - IMDInternalImport *m_pMDImport_UseAccessor; -#endif - IMetaDataImport2 *m_pImporter; - IMetaDataEmit *m_pEmitter; - SimpleRWLock *m_pMetadataLock; - Volatile m_refCount; - int m_flags; - -public: - - PTR_PEImage GetILimage() - { - CONTRACTL - { - THROWS; - MODE_ANY; - GC_TRIGGERS; - } - CONTRACTL_END; -#ifndef DACCESS_COMPILE - if (m_openedILimage == NULL && m_identity != NULL) - { - PEImage* pOpenedILimage; - m_identity->Clone(MDInternalImport_Default,&pOpenedILimage); - if (InterlockedCompareExchangeT(&m_openedILimage,pOpenedILimage,NULL) != NULL) - pOpenedILimage->Release(); - } -#endif - return m_openedILimage; - } - - PEImage *GetOpenedILimage() - { - LIMITED_METHOD_DAC_CONTRACT; - _ASSERTE(HasOpenedILimage()); - return m_openedILimage; - } - - - BOOL HasOpenedILimage() - { - LIMITED_METHOD_DAC_CONTRACT; - return m_openedILimage != NULL; - - } - - BOOL HasLoadedIL() + bool HasHostAssembly() { - LIMITED_METHOD_DAC_CONTRACT; - return HasOpenedILimage() && GetOpenedILimage()->HasLoadedLayout(); + STATIC_CONTRACT_WRAPPER; + return GetHostAssembly() != NULL; } - LPCWSTR GetPathForErrorMessages(); - - static PEFile* Dummy(); - - void ConvertMDInternalToReadWrite(); - -protected: - PTR_BINDER_SPACE_Assembly m_pHostAssembly; - - // For certain assemblies, we do not have m_pHostAssembly since they are not bound using an actual binder. - // An example is Ref-Emitted assemblies. Thus, when such assemblies trigger load of their dependencies, - // we need to ensure they are loaded in appropriate load context. - // - // To enable this, we maintain a concept of "FallbackBinder", which will be set to the Binder of the - // assembly that created the dynamic assembly. If the creator assembly is dynamic itself, then its fallback - // load context would be propagated to the assembly being dynamically generated. - PTR_AssemblyBinder m_pFallbackBinder; - -public: // Returns a non-AddRef'ed BINDER_SPACE::Assembly* PTR_BINDER_SPACE_Assembly GetHostAssembly() { @@ -467,8 +317,9 @@ class PEFile return m_pHostAssembly; } - // Returns the AssemblyBinder* instance associated with the PEFile - // which owns the context into which the current PEFile was loaded. + // Returns the AssemblyBinder* instance associated with the PEAssembly + // which owns the context into which the current PEAssembly was loaded. + // For Dynamic assemblies this is the fallback binder. PTR_AssemblyBinder GetAssemblyBinder(); #ifndef DACCESS_COMPILE @@ -480,8 +331,7 @@ class PEFile #endif //!DACCESS_COMPILE - bool HasHostAssembly() - { STATIC_CONTRACT_WRAPPER; return GetHostAssembly() != nullptr; } + ULONG HashIdentity(); PTR_AssemblyBinder GetFallbackBinder() { @@ -489,111 +339,122 @@ class PEFile return m_pFallbackBinder; } -}; // class PEFile - - -class PEAssembly : public PEFile -{ - VPTR_VTABLE_CLASS(PEAssembly, PEFile) - public: // ------------------------------------------------------------ - // Statics initialization. - // ------------------------------------------------------------ - static - void Attach(); - - // ------------------------------------------------------------ - // Public API + // Creation entry points // ------------------------------------------------------------ // CoreCLR's PrivBinder PEAssembly creation entrypoint - static PEAssembly * Open( - PEAssembly * pParent, - PEImage * pPEImageIL, - BINDER_SPACE::Assembly * pHostAssembly); + static PEAssembly* Open( + PEAssembly* pParent, + PEImage* pPEImageIL, + BINDER_SPACE::Assembly* pHostAssembly); // This opens the canonical System.Private.CoreLib.dll - static PEAssembly *OpenSystem(); -#ifdef DACCESS_COMPILE - virtual void EnumMemoryRegions(CLRDataEnumMemoryFlags flags); -#endif + static PEAssembly* OpenSystem(); - static PEAssembly *Open( - BINDER_SPACE::Assembly* pBindResult, - BOOL isSystem); + static PEAssembly* Open(BINDER_SPACE::Assembly* pBindResult); - static PEAssembly *Create( - PEAssembly *pParentAssembly, - IMetaDataAssemblyEmit *pEmit); + static PEAssembly* Create( + PEAssembly* pParentAssembly, + IMetaDataAssemblyEmit* pEmit); - private: - // Private helpers for crufty exception handling reasons - static PEAssembly *DoOpenSystem(); + // ------------------------------------------------------------ + // Utility functions + // ------------------------------------------------------------ - public: + static void PathToUrl(SString& string); + static void UrlToPath(SString& string); + static BOOL FindLastPathSeparator(const SString& path, SString::Iterator& i); +private: // ------------------------------------------------------------ - // binding & source + // Loader access API // ------------------------------------------------------------ - ULONG HashIdentity(); + // Private helper for crufty exception handling reasons + static PEAssembly* DoOpenSystem(); // ------------------------------------------------------------ - // Descriptive strings + // Internal routines // ------------------------------------------------------------ - // This returns a non-empty path representing the source of the assembly; it may - // be the parent assembly for dynamic or memory assemblies - const SString &GetEffectivePath(); +#ifdef DACCESS_COMPILE + // just to make the DAC and GCC happy. + virtual ~PEAssembly() {}; + PEAssembly() = default; +#else + PEAssembly( + BINDER_SPACE::Assembly* pBindResultInfo, + IMetaDataEmit* pEmit, + PEAssembly* creator, + BOOL isSystem, + PEImage* pPEImageIL = NULL, + BINDER_SPACE::Assembly* pHostAssembly = NULL + ); - // Codebase is the fusion codebase or path for the assembly. It is in URL format. - // Note this may be obtained from the parent PEFile if we don't have a path or fusion - // assembly. - BOOL GetCodeBase(SString &result); + virtual ~PEAssembly(); +#endif - // Display name is the fusion binding name for an assembly - void GetDisplayName(SString &result, DWORD flags = 0); + void OpenMDImport(); + void OpenImporter(); + void OpenEmitter(); - // ------------------------------------------------------------ - // Metadata access - // ------------------------------------------------------------ +private: - LPCUTF8 GetSimpleName(); + // ------------------------------------------------------------ + // Instance fields + // ------------------------------------------------------------ - // ------------------------------------------------------------ - // Utility functions - // ------------------------------------------------------------ +#ifdef _DEBUG + LPCWSTR m_pDebugName; + SString m_debugName; +#endif - static void PathToUrl(SString &string); - static void UrlToPath(SString &string); - static BOOL FindLastPathSeparator(const SString &path, SString::Iterator &i); + // IL image, NULL if dynamic + PTR_PEImage m_PEImage; - protected: + PTR_PEAssembly m_creator; + // This flag is not updated atomically with m_pMDImport. Its fine for debugger usage + // but don't rely on it in the runtime. In runtime try QI'ing the m_pMDImport for + // IID_IMDInternalImportENC + BOOL m_MDImportIsRW_Debugger_Use_Only; + union + { #ifndef DACCESS_COMPILE - PEAssembly( - BINDER_SPACE::Assembly* pBindResultInfo, - IMetaDataEmit *pEmit, - PEFile *creator, - BOOL system, - PEImage * pPEImageIL = NULL, - BINDER_SPACE::Assembly * pHostAssembly = NULL - ); - virtual ~PEAssembly(); + IMDInternalImport* m_pMDImport; +#else + // NB: m_pMDImport_UseAccessor appears to be never assigned a value, but its purpose is just + // to be a placeholder that has the same type and offset as m_pMDImport. + // + // The field has a different name so it would be an error to use directly. + // Only GetMDInternalRWAddress is supposed to use it via (TADDR)m_pMDImport_UseAccessor, + // which at that point will match the m_pMDImport on the debuggee side. + // See more scary comments in GetMDInternalRWAddress. + IMDInternalImport* m_pMDImport_UseAccessor; #endif + }; - private: - // ------------------------------------------------------------ - // Instance fields - // ------------------------------------------------------------ + IMetaDataImport2* m_pImporter; + IMetaDataEmit* m_pEmitter; - PTR_PEFile m_creator; -}; + Volatile m_refCount; + bool m_isSystem; + PTR_BINDER_SPACE_Assembly m_pHostAssembly; + + // For certain assemblies, we do not have m_pHostAssembly since they are not bound using an actual binder. + // An example is Ref-Emitted assemblies. Thus, when such assemblies trigger load of their dependencies, + // we need to ensure they are loaded in appropriate load context. + // + // To enable this, we maintain a concept of "FallbackBinder", which will be set to the Binder of the + // assembly that created the dynamic assembly. If the creator assembly is dynamic itself, then its fallback + // load context would be propagated to the assembly being dynamically generated. + PTR_AssemblyBinder m_pFallbackBinder; -typedef ReleaseHolder PEFileHolder; +}; // class PEAssembly typedef ReleaseHolder PEAssemblyHolder; -#endif // PEFILE_H_ +#endif // PEASSEMBLY_H_ diff --git a/src/coreclr/vm/pefile.inl b/src/coreclr/vm/peassembly.inl similarity index 56% rename from src/coreclr/vm/pefile.inl rename to src/coreclr/vm/peassembly.inl index e00136064bc1e..42102f13a05da 100644 --- a/src/coreclr/vm/pefile.inl +++ b/src/coreclr/vm/peassembly.inl @@ -1,13 +1,13 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // -------------------------------------------------------------------------------- -// PEFile.inl +// PEAssembly.inl // // -------------------------------------------------------------------------------- -#ifndef PEFILE_INL_ -#define PEFILE_INL_ +#ifndef PEASSEMBLY_INL_ +#define PEASSEMBLY_INL_ #include "check.h" #include "simplerwlock.hpp" @@ -15,7 +15,7 @@ #include "peimagelayout.inl" #if CHECK_INVARIANTS -inline CHECK PEFile::Invariant() +inline CHECK PEAssembly::Invariant() { CONTRACT_CHECK { @@ -29,14 +29,12 @@ inline CHECK PEFile::Invariant() if (IsDynamic()) { // dynamic module case - CHECK(m_openedILimage == NULL); + CHECK(m_PEImage == NULL); CHECK(CheckPointer(m_pEmitter)); } else { - // If m_image is null, then we should have a native image. However, this is not valid initially - // during construction. We should find a way to assert this. - CHECK(CheckPointer((PEImage*) m_openedILimage, NULL_OK)); + CHECK(CheckPointer((PEImage*)m_PEImage)); } CHECK_OK; } @@ -46,7 +44,7 @@ inline CHECK PEFile::Invariant() // AddRef/Release // ------------------------------------------------------------ -inline ULONG PEFile::AddRef() +inline ULONG PEAssembly::AddRef() { CONTRACTL { @@ -61,7 +59,7 @@ inline ULONG PEFile::AddRef() return FastInterlockIncrement(&m_refCount); } -inline ULONG PEFile::Release() +inline ULONG PEAssembly::Release() { CONTRACT(COUNT_T) { @@ -88,7 +86,7 @@ inline ULONG PEAssembly::HashIdentity() { CONTRACTL { - PRECONDITION(CheckPointer(m_identity)); + PRECONDITION(CheckPointer(m_PEImage)); MODE_ANY; THROWS; GC_TRIGGERS; @@ -97,7 +95,7 @@ inline ULONG PEAssembly::HashIdentity() return m_pHostAssembly->GetAssemblyName()->Hash(BINDER_SPACE::AssemblyName::INCLUDE_VERSION); } -inline void PEFile::ValidateForExecution() +inline void PEAssembly::ValidateForExecution() { CONTRACTL { @@ -110,7 +108,7 @@ inline void PEFile::ValidateForExecution() // // Ensure reference assemblies are not loaded for execution // - ReleaseHolder mdImport(this->GetMDImportWithRef()); + IMDInternalImport* mdImport = GetMDImport(); if (mdImport->GetCustomAttributeByName(TokenFromRid(1, mdtAssembly), g_ReferenceAssemblyAttribute, NULL, @@ -121,7 +119,7 @@ inline void PEFile::ValidateForExecution() // // Ensure platform is valid for execution // - if (!IsDynamic() && !IsResource()) + if (!IsDynamic()) { if (IsMarkedAsNoPlatform()) { @@ -131,14 +129,14 @@ inline void PEFile::ValidateForExecution() } -inline BOOL PEFile::IsMarkedAsNoPlatform() +inline BOOL PEAssembly::IsMarkedAsNoPlatform() { WRAPPER_NO_CONTRACT; return (IsAfPA_NoPlatform(GetFlags())); } -inline void PEFile::GetMVID(GUID *pMvid) +inline void PEAssembly::GetMVID(GUID *pMvid) { CONTRACTL { @@ -149,14 +147,14 @@ inline void PEFile::GetMVID(GUID *pMvid) } CONTRACTL_END; - IfFailThrow(GetPersistentMDImport()->GetScopeProps(NULL, pMvid)); + IfFailThrow(GetMDImport()->GetScopeProps(NULL, pMvid)); } // ------------------------------------------------------------ // Descriptive strings // ------------------------------------------------------------ -inline const SString& PEFile::GetPath() +inline const SString& PEAssembly::GetPath() { CONTRACTL { @@ -169,17 +167,18 @@ inline const SString& PEFile::GetPath() } CONTRACTL_END; - if (IsDynamic() || m_identity->IsInBundle ()) + if (IsDynamic() || m_PEImage->IsInBundle ()) { return SString::Empty(); } - return m_identity->GetPath(); + + return m_PEImage->GetPath(); } // // Returns the identity path even for single-file/bundled apps. // -inline const SString& PEFile::GetIdentityPath() +inline const SString& PEAssembly::GetIdentityPath() { CONTRACTL { @@ -192,15 +191,16 @@ inline const SString& PEFile::GetIdentityPath() } CONTRACTL_END; - if (m_identity == nullptr) + if (m_PEImage == nullptr) { return SString::Empty(); } - return m_identity->GetPath(); + + return m_PEImage->GetPath(); } #ifdef DACCESS_COMPILE -inline const SString &PEFile::GetModuleFileNameHint() +inline const SString &PEAssembly::GetModuleFileNameHint() { CONTRACTL { @@ -216,12 +216,12 @@ inline const SString &PEFile::GetModuleFileNameHint() return SString::Empty(); } else - return m_identity->GetModuleFileNameHintForDAC(); + return m_PEImage->GetModuleFileNameHintForDAC(); } #endif // DACCESS_COMPILE #ifdef LOGGING -inline LPCWSTR PEFile::GetDebugName() +inline LPCWSTR PEAssembly::GetDebugName() { CONTRACTL { @@ -245,108 +245,29 @@ inline LPCWSTR PEFile::GetDebugName() // Classification // ------------------------------------------------------------ -inline BOOL PEFile::IsAssembly() const -{ - LIMITED_METHOD_DAC_CONTRACT; - - return (m_flags & PEFILE_ASSEMBLY) != 0; -} - -inline PTR_PEAssembly PEFile::AsAssembly() -{ - LIMITED_METHOD_DAC_CONTRACT; - - if (IsAssembly()) - return dac_cast(this); - else - return dac_cast(nullptr); -} - -inline BOOL PEFile::IsSystem() const +inline BOOL PEAssembly::IsSystem() const { LIMITED_METHOD_CONTRACT; SUPPORTS_DAC; - return (m_flags & PEFILE_SYSTEM) != 0; + return m_isSystem; } -inline BOOL PEFile::IsDynamic() const +inline BOOL PEAssembly::IsDynamic() const { LIMITED_METHOD_CONTRACT; SUPPORTS_DAC; - return m_identity == NULL; -} - -inline BOOL PEFile::IsResource() const -{ - WRAPPER_NO_CONTRACT; - SUPPORTS_DAC; - - return FALSE; -} - -inline BOOL PEFile::IsIStream() const -{ - LIMITED_METHOD_CONTRACT; - - return FALSE; -} - -inline PEAssembly *PEFile::GetAssembly() const -{ - WRAPPER_NO_CONTRACT; - _ASSERTE(IsAssembly()); - return dac_cast(this); + return m_PEImage == NULL; } // ------------------------------------------------------------ // Metadata access // ------------------------------------------------------------ - -inline BOOL PEFile::HasMetadata() -{ - CONTRACTL - { - INSTANCE_CHECK; - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - SUPPORTS_DAC; - } - CONTRACTL_END; - - return !IsResource(); -} - -inline IMDInternalImportHolder PEFile::GetMDImport() +inline IMDInternalImport* PEAssembly::GetMDImport() { WRAPPER_NO_CONTRACT; - if (m_bHasPersistentMDImport) - return IMDInternalImportHolder(GetPersistentMDImport(),FALSE); - else - return IMDInternalImportHolder(GetMDImportWithRef(),TRUE); -}; -inline IMDInternalImport* PEFile::GetPersistentMDImport() -{ -/* - CONTRACT(IMDInternalImport *) - { - INSTANCE_CHECK; - PRECONDITION(!IsResource()); - POSTCONDITION(CheckPointer(RETVAL)); - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACT_END; -*/ - SUPPORTS_DAC; -#if !defined(__GNUC__) - - _ASSERTE(!IsResource()); -#endif #ifdef DACCESS_COMPILE WRAPPER_NO_CONTRACT; return DacGetMDImport(this, true); @@ -355,56 +276,16 @@ inline IMDInternalImport* PEFile::GetPersistentMDImport() return m_pMDImport; #endif -} - -inline IMDInternalImport *PEFile::GetMDImportWithRef() -{ -/* - CONTRACT(IMDInternalImport *) - { - INSTANCE_CHECK; - PRECONDITION(!IsResource()); - POSTCONDITION(CheckPointer(RETVAL)); - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACT_END; -*/ -#if !defined(__GNUC__) - _ASSERTE(!IsResource()); -#endif -#ifdef DACCESS_COMPILE - WRAPPER_NO_CONTRACT; - return DacGetMDImport(this, true); -#else - CONTRACTL - { - NOTHROW; - WRAPPER(GC_TRIGGERS); - MODE_ANY; - CAN_TAKE_LOCK; - } - CONTRACTL_END; - - GCX_PREEMP(); - SimpleReadLockHolder lock(m_pMetadataLock); - if(m_pMDImport) - m_pMDImport->AddRef(); - return m_pMDImport; -#endif -} +}; #ifndef DACCESS_COMPILE -inline IMetaDataImport2 *PEFile::GetRWImporter() +inline IMetaDataImport2 *PEAssembly::GetRWImporter() { CONTRACT(IMetaDataImport2 *) { INSTANCE_CHECK; - PRECONDITION(!IsResource()); POSTCONDITION(CheckPointer(RETVAL)); - PRECONDITION(m_bHasPersistentMDImport); GC_NOTRIGGER; THROWS; MODE_ANY; @@ -417,16 +298,14 @@ inline IMetaDataImport2 *PEFile::GetRWImporter() RETURN m_pImporter; } -inline IMetaDataEmit *PEFile::GetEmitter() +inline IMetaDataEmit *PEAssembly::GetEmitter() { CONTRACT(IMetaDataEmit *) { INSTANCE_CHECK; MODE_ANY; GC_NOTRIGGER; - PRECONDITION(!IsResource()); POSTCONDITION(CheckPointer(RETVAL)); - PRECONDITION(m_bHasPersistentMDImport); THROWS; } CONTRACT_END; @@ -440,40 +319,10 @@ inline IMetaDataEmit *PEFile::GetEmitter() #endif // DACCESS_COMPILE -// The simple name is not actually very simple. The name returned comes from one of -// various metadata tables, depending on whether this is a manifest module, -// non-manifest module, or something else -inline LPCUTF8 PEFile::GetSimpleName() -{ - CONTRACT(LPCUTF8) - { - INSTANCE_CHECK; - MODE_ANY; - POSTCONDITION(CheckPointer(RETVAL)); - NOTHROW; - SUPPORTS_DAC; - WRAPPER(GC_TRIGGERS); - } - CONTRACT_END; - - if (IsAssembly()) - RETURN dac_cast(this)->GetSimpleName(); - else - { - LPCUTF8 szScopeName; - if (FAILED(GetScopeName(&szScopeName))) - { - szScopeName = ""; - } - RETURN szScopeName; - } -} - - // Same as the managed Module.ScopeName property, this unconditionally looks in the // metadata Module table to get the name. Useful for profilers and others who don't // like sugar coating on their names. -inline HRESULT PEFile::GetScopeName(LPCUTF8 * pszName) +inline HRESULT PEAssembly::GetScopeName(LPCUTF8 * pszName) { CONTRACTL { @@ -493,14 +342,7 @@ inline HRESULT PEFile::GetScopeName(LPCUTF8 * pszName) // PE file access // ------------------------------------------------------------ -inline BOOL PEFile::IsIbcOptimized() -{ - WRAPPER_NO_CONTRACT; - - return FALSE; -} - -inline BOOL PEFile::IsReadyToRun() +inline BOOL PEAssembly::IsReadyToRun() { CONTRACTL { @@ -511,9 +353,9 @@ inline BOOL PEFile::IsReadyToRun() } CONTRACTL_END; - if (HasOpenedILimage()) + if (HasPEImage()) { - return GetLoadedIL()->HasReadyToRunHeader(); + return GetLoadedLayout()->HasReadyToRunHeader(); } else { @@ -521,63 +363,37 @@ inline BOOL PEFile::IsReadyToRun() } } -inline WORD PEFile::GetSubsystem() -{ - WRAPPER_NO_CONTRACT; - - if (IsResource() || IsDynamic()) - return 0; - - return GetLoadedIL()->GetSubsystem(); -} - -inline mdToken PEFile::GetEntryPointToken( -#ifdef _DEBUG - BOOL bAssumeLoaded -#endif //_DEBUG - ) +inline mdToken PEAssembly::GetEntryPointToken() { WRAPPER_NO_CONTRACT; - if (IsResource() || IsDynamic()) + if (IsDynamic()) return mdTokenNil; - _ASSERTE (!bAssumeLoaded || HasLoadedIL ()); - return GetOpenedILimage()->GetEntryPointToken(); + return GetPEImage()->GetEntryPointToken(); } -inline BOOL PEFile::IsILOnly() +inline BOOL PEAssembly::IsILOnly() { WRAPPER_NO_CONTRACT; SUPPORTS_DAC; CONTRACT_VIOLATION(ThrowsViolation|GCViolation|FaultViolation); - if (IsResource() || IsDynamic()) + if (IsDynamic()) return FALSE; - return GetOpenedILimage()->IsILOnly(); -} - -inline BOOL PEFile::IsDll() -{ - WRAPPER_NO_CONTRACT; - - if (IsResource() || IsDynamic()) - return TRUE; - - return GetOpenedILimage()->IsDll(); + return GetPEImage()->IsILOnly(); } -inline PTR_VOID PEFile::GetRvaField(RVA field) +inline PTR_VOID PEAssembly::GetRvaField(RVA field) { CONTRACT(void *) { INSTANCE_CHECK; PRECONDITION(!IsDynamic()); - PRECONDITION(!IsResource()); PRECONDITION(CheckRvaField(field)); - PRECONDITION(CheckLoaded()); + PRECONDITION(HasLoadedPEImage()); NOTHROW; GC_NOTRIGGER; MODE_ANY; @@ -589,17 +405,16 @@ inline PTR_VOID PEFile::GetRvaField(RVA field) // Note that the native image Rva fields are currently cut off before // this point. We should not get here for an IL only native image. - RETURN dac_cast(GetLoadedIL()->GetRvaData(field,NULL_OK)); + RETURN dac_cast(GetLoadedLayout()->GetRvaData(field,NULL_OK)); } -inline CHECK PEFile::CheckRvaField(RVA field) +inline CHECK PEAssembly::CheckRvaField(RVA field) { CONTRACT_CHECK { INSTANCE_CHECK; PRECONDITION(!IsDynamic()); - PRECONDITION(!IsResource()); - PRECONDITION(CheckLoaded()); + PRECONDITION(HasLoadedPEImage()); NOTHROW; GC_NOTRIGGER; MODE_ANY; @@ -609,18 +424,17 @@ inline CHECK PEFile::CheckRvaField(RVA field) // Note that the native image Rva fields are currently cut off before // this point. We should not get here for an IL only native image. - CHECK(GetLoadedIL()->CheckRva(field,NULL_OK)); + CHECK(GetLoadedLayout()->CheckRva(field,NULL_OK)); CHECK_OK; } -inline CHECK PEFile::CheckRvaField(RVA field, COUNT_T size) +inline CHECK PEAssembly::CheckRvaField(RVA field, COUNT_T size) { CONTRACT_CHECK { INSTANCE_CHECK; PRECONDITION(!IsDynamic()); - PRECONDITION(!IsResource()); - PRECONDITION(CheckLoaded()); + PRECONDITION(HasLoadedPEImage()); NOTHROW; GC_NOTRIGGER; MODE_ANY; @@ -630,11 +444,11 @@ inline CHECK PEFile::CheckRvaField(RVA field, COUNT_T size) // Note that the native image Rva fields are currently cut off before // this point. We should not get here for an IL only native image. - CHECK(GetLoadedIL()->CheckRva(field, size,0,NULL_OK)); + CHECK(GetLoadedLayout()->CheckRva(field, size,0,NULL_OK)); CHECK_OK; } -inline BOOL PEFile::HasTls() +inline BOOL PEAssembly::HasTls() { CONTRACTL { @@ -642,24 +456,21 @@ inline BOOL PEFile::HasTls() NOTHROW; GC_NOTRIGGER; MODE_ANY; - PRECONDITION(CheckLoaded()); + PRECONDITION(IsLoaded()); } CONTRACTL_END; - // Resource modules do not contain TLS data. - if (IsResource()) - return FALSE; // Dynamic modules do not contain TLS data. - else if (IsDynamic()) + if (IsDynamic()) return FALSE; // ILOnly modules do not contain TLS data. else if (IsILOnly()) return FALSE; else - return GetLoadedIL()->HasTls(); + return GetLoadedLayout()->HasTls(); } -inline BOOL PEFile::IsRvaFieldTls(RVA field) +inline BOOL PEAssembly::IsRvaFieldTls(RVA field) { CONTRACTL { @@ -667,30 +478,30 @@ inline BOOL PEFile::IsRvaFieldTls(RVA field) NOTHROW; GC_NOTRIGGER; MODE_ANY; - PRECONDITION(CheckLoaded()); + PRECONDITION(IsLoaded()); } CONTRACTL_END; if (!HasTls()) return FALSE; - PTR_VOID address = PTR_VOID(GetLoadedIL()->GetRvaData(field)); + PTR_VOID address = PTR_VOID(GetLoadedLayout()->GetRvaData(field)); COUNT_T tlsSize; - PTR_VOID tlsRange = GetLoadedIL()->GetTlsRange(&tlsSize); + PTR_VOID tlsRange = GetLoadedLayout()->GetTlsRange(&tlsSize); return (address >= tlsRange && address < (dac_cast(tlsRange)+tlsSize)); } -inline UINT32 PEFile::GetFieldTlsOffset(RVA field) +inline UINT32 PEAssembly::GetFieldTlsOffset(RVA field) { CONTRACTL { INSTANCE_CHECK; PRECONDITION(CheckRvaField(field)); PRECONDITION(IsRvaFieldTls(field)); - PRECONDITION(CheckLoaded()); + PRECONDITION(HasLoadedPEImage()); NOTHROW; GC_NOTRIGGER; MODE_ANY; @@ -698,14 +509,14 @@ inline UINT32 PEFile::GetFieldTlsOffset(RVA field) CONTRACTL_END; return (UINT32)(dac_cast(GetRvaField(field)) - - dac_cast(GetLoadedIL()->GetTlsRange())); + dac_cast(GetLoadedLayout()->GetTlsRange())); } -inline UINT32 PEFile::GetTlsIndex() +inline UINT32 PEAssembly::GetTlsIndex() { CONTRACTL { - PRECONDITION(CheckLoaded()); + PRECONDITION(HasLoadedPEImage()); INSTANCE_CHECK; PRECONDITION(HasTls()); NOTHROW; @@ -714,18 +525,17 @@ inline UINT32 PEFile::GetTlsIndex() } CONTRACTL_END; - return GetLoadedIL()->GetTlsIndex(); + return GetLoadedLayout()->GetTlsIndex(); } -inline const void *PEFile::GetInternalPInvokeTarget(RVA target) +inline const void *PEAssembly::GetInternalPInvokeTarget(RVA target) { CONTRACT(void *) { INSTANCE_CHECK; PRECONDITION(!IsDynamic()); - PRECONDITION(!IsResource()); PRECONDITION(CheckInternalPInvokeTarget(target)); - PRECONDITION(CheckLoaded()); + PRECONDITION(HasLoadedPEImage()); NOTHROW; GC_NOTRIGGER; MODE_ANY; @@ -733,17 +543,16 @@ inline const void *PEFile::GetInternalPInvokeTarget(RVA target) } CONTRACT_END; - RETURN (void*)GetLoadedIL()->GetRvaData(target); + RETURN (void*)GetLoadedLayout()->GetRvaData(target); } -inline CHECK PEFile::CheckInternalPInvokeTarget(RVA target) +inline CHECK PEAssembly::CheckInternalPInvokeTarget(RVA target) { CONTRACT_CHECK { INSTANCE_CHECK; PRECONDITION(!IsDynamic()); - PRECONDITION(!IsResource()); - PRECONDITION(CheckLoaded()); + PRECONDITION(HasLoadedPEImage()); NOTHROW; GC_NOTRIGGER; MODE_ANY; @@ -751,16 +560,16 @@ inline CHECK PEFile::CheckInternalPInvokeTarget(RVA target) CONTRACT_CHECK_END; CHECK(!IsILOnly()); - CHECK(GetLoadedIL()->CheckRva(target)); + CHECK(GetLoadedLayout()->CheckRva(target)); CHECK_OK; } -inline IMAGE_COR_VTABLEFIXUP *PEFile::GetVTableFixups(COUNT_T *pCount/*=NULL*/) +inline IMAGE_COR_VTABLEFIXUP *PEAssembly::GetVTableFixups(COUNT_T *pCount/*=NULL*/) { CONTRACT(IMAGE_COR_VTABLEFIXUP *) { - PRECONDITION(CheckLoaded()); + PRECONDITION(HasLoadedPEImage()); INSTANCE_CHECK; NOTHROW; GC_NOTRIGGER; @@ -769,26 +578,25 @@ inline IMAGE_COR_VTABLEFIXUP *PEFile::GetVTableFixups(COUNT_T *pCount/*=NULL*/) } CONTRACT_END; - if (IsResource() || IsDynamic() || IsILOnly()) + if (IsDynamic() || IsILOnly()) { if (pCount != NULL) *pCount = 0; RETURN NULL; } else - RETURN GetLoadedIL()->GetVTableFixups(pCount); + RETURN GetLoadedLayout()->GetVTableFixups(pCount); } -inline void *PEFile::GetVTable(RVA rva) +inline void *PEAssembly::GetVTable(RVA rva) { CONTRACT(void *) { INSTANCE_CHECK; PRECONDITION(!IsDynamic()); - PRECONDITION(!IsResource()); - PRECONDITION(CheckLoaded()); + PRECONDITION(HasLoadedPEImage()); PRECONDITION(!IsILOnly()); - PRECONDITION(GetLoadedIL()->CheckRva(rva)); + PRECONDITION(GetLoadedLayout()->CheckRva(rva)); NOTHROW; GC_NOTRIGGER; MODE_ANY; @@ -796,18 +604,17 @@ inline void *PEFile::GetVTable(RVA rva) } CONTRACT_END; - RETURN (void *)GetLoadedIL()->GetRvaData(rva); + RETURN (void *)GetLoadedLayout()->GetRvaData(rva); } // @todo: this is bad to expose. But it is needed to support current IJW thunks -inline HMODULE PEFile::GetIJWBase() +inline HMODULE PEAssembly::GetIJWBase() { CONTRACTL { INSTANCE_CHECK; PRECONDITION(!IsDynamic()); - PRECONDITION(!IsResource()); - PRECONDITION(CheckLoaded()); + PRECONDITION(HasLoadedPEImage()); PRECONDITION(!IsILOnly()); NOTHROW; GC_NOTRIGGER; @@ -815,10 +622,10 @@ inline HMODULE PEFile::GetIJWBase() } CONTRACTL_END; - return (HMODULE) dac_cast(GetLoadedIL()->GetBase()); + return (HMODULE) dac_cast(GetLoadedLayout()->GetBase()); } -inline PTR_VOID PEFile::GetDebuggerContents(COUNT_T *pSize/*=NULL*/) +inline PTR_VOID PEAssembly::GetDebuggerContents(COUNT_T *pSize/*=NULL*/) { CONTRACT(PTR_VOID) { @@ -835,12 +642,12 @@ inline PTR_VOID PEFile::GetDebuggerContents(COUNT_T *pSize/*=NULL*/) // helper thread. The debugger will have to expect a zero base // in some circumstances. - if (IsLoaded()) + if (HasLoadedPEImage()) { if (pSize != NULL) - *pSize = GetLoaded()->GetSize(); + *pSize = GetLoadedLayout()->GetSize(); - RETURN GetLoaded()->GetBase(); + RETURN GetLoadedLayout()->GetBase(); } else { @@ -851,7 +658,7 @@ inline PTR_VOID PEFile::GetDebuggerContents(COUNT_T *pSize/*=NULL*/) } } -inline PTR_CVOID PEFile::GetLoadedImageContents(COUNT_T *pSize/*=NULL*/) +inline PTR_CVOID PEAssembly::GetLoadedImageContents(COUNT_T *pSize/*=NULL*/) { CONTRACTL { @@ -863,13 +670,13 @@ inline PTR_CVOID PEFile::GetLoadedImageContents(COUNT_T *pSize/*=NULL*/) } CONTRACTL_END; - if (IsLoaded() && !IsDynamic()) + if (HasLoadedPEImage()) { if (pSize != NULL) { - *pSize = GetLoaded()->GetSize(); + *pSize = GetLoadedLayout()->GetSize(); } - return GetLoaded()->GetBase(); + return GetLoadedLayout()->GetBase(); } else { @@ -882,32 +689,32 @@ inline PTR_CVOID PEFile::GetLoadedImageContents(COUNT_T *pSize/*=NULL*/) } #ifndef DACCESS_COMPILE -inline const void *PEFile::GetManagedFileContents(COUNT_T *pSize/*=NULL*/) +inline const void *PEAssembly::GetManagedFileContents(COUNT_T *pSize/*=NULL*/) { CONTRACT(const void *) { INSTANCE_CHECK; - PRECONDITION(CheckLoaded()); + PRECONDITION(HasLoadedPEImage()); WRAPPER(THROWS); WRAPPER(GC_TRIGGERS); MODE_ANY; - POSTCONDITION((!GetLoaded()->GetSize()) || CheckPointer(RETVAL)); + POSTCONDITION((!GetLoadedLayout()->GetSize()) || CheckPointer(RETVAL)); } CONTRACT_END; // Right now, we will trigger a LoadLibrary for the caller's sake, // even if we are in a scenario where we could normally avoid it. - LoadLibrary(FALSE); + EnsureLoaded(); if (pSize != NULL) - *pSize = GetLoadedIL()->GetSize(); + *pSize = GetLoadedLayout()->GetSize(); - RETURN GetLoadedIL()->GetBase(); + RETURN GetLoadedLayout()->GetBase(); } #endif // DACCESS_COMPILE -inline BOOL PEFile::IsPtrInILImage(PTR_CVOID data) +inline BOOL PEAssembly::IsPtrInPEImage(PTR_CVOID data) { CONTRACTL { @@ -919,49 +726,13 @@ inline BOOL PEFile::IsPtrInILImage(PTR_CVOID data) } CONTRACTL_END; - if (HasOpenedILimage()) + if (HasPEImage()) { - return GetOpenedILimage()->IsPtrInImage(data); + return GetPEImage()->IsPtrInImage(data); } else return FALSE; } -// ------------------------------------------------------------ -// Native image access -// ------------------------------------------------------------ - -inline PTR_PEImageLayout PEFile::GetLoadedIL() -{ - LIMITED_METHOD_CONTRACT; - SUPPORTS_DAC; - - _ASSERTE(HasOpenedILimage()); - - return GetOpenedILimage()->GetLoadedLayout(); -}; - -inline BOOL PEFile::IsLoaded(BOOL bAllowNative/*=TRUE*/) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - if(IsDynamic()) - return TRUE; - return HasLoadedIL(); -}; - - -inline PTR_PEImageLayout PEFile::GetLoaded() -{ - WRAPPER_NO_CONTRACT; - SUPPORTS_DAC; - return GetLoadedIL(); -}; - // ------------------------------------------------------------ // Descriptive strings @@ -995,14 +766,14 @@ inline LPCSTR PEAssembly::GetSimpleName() CONTRACTL { NOTHROW; - if (!m_bHasPersistentMDImport) { GC_TRIGGERS;} else {DISABLED(GC_TRIGGERS);}; + DISABLED(GC_TRIGGERS); MODE_ANY; SUPPORTS_DAC; } CONTRACTL_END; LPCSTR name = ""; - IMDInternalImportHolder pImport = GetMDImport(); + IMDInternalImport* pImport = GetMDImport(); if (pImport != NULL) { if (FAILED(pImport->GetAssemblyProps(TokenFromRid(1, mdtAssembly), NULL, NULL, NULL, &name, NULL, NULL))) @@ -1014,7 +785,7 @@ inline LPCSTR PEAssembly::GetSimpleName() return name; } -inline BOOL PEFile::IsStrongNamed() +inline BOOL PEAssembly::IsStrongNamed() { CONTRACTL { @@ -1035,7 +806,7 @@ inline BOOL PEFile::IsStrongNamed() // Check to see if this assembly has had its strong name signature verified yet. // -inline const void *PEFile::GetPublicKey(DWORD *pcbPK) +inline const void *PEAssembly::GetPublicKey(DWORD *pcbPK) { CONTRACTL { @@ -1051,7 +822,7 @@ inline const void *PEFile::GetPublicKey(DWORD *pcbPK) return pPK; } -inline ULONG PEFile::GetHashAlgId() +inline ULONG PEAssembly::GetHashAlgId() { CONTRACTL { @@ -1066,7 +837,7 @@ inline ULONG PEFile::GetHashAlgId() return hashAlgId; } -inline LPCSTR PEFile::GetLocale() +inline LPCSTR PEAssembly::GetLocale() { CONTRACTL { @@ -1081,11 +852,10 @@ inline LPCSTR PEFile::GetLocale() return md.szLocale; } -inline DWORD PEFile::GetFlags() +inline DWORD PEAssembly::GetFlags() { CONTRACTL { - PRECONDITION(IsAssembly()); INSTANCE_CHECK; if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS; if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS; @@ -1099,43 +869,4 @@ inline DWORD PEFile::GetFlags() return flags; } -// In the cases where you know the module is loaded, and cannot tolerate triggering and -// loading, this alternative to PEFile::GetFlags is useful. Profiling API uses this. -inline HRESULT PEFile::GetFlagsNoTrigger(DWORD * pdwFlags) -{ - CONTRACTL - { - PRECONDITION(IsAssembly()); - INSTANCE_CHECK; - NOTHROW; - GC_NOTRIGGER; - FORBID_FAULT; - MODE_ANY; - } - CONTRACTL_END; - - _ASSERTE (pdwFlags != NULL); - - if (!m_bHasPersistentMDImport) - return E_FAIL; - - return GetPersistentMDImport()->GetAssemblyProps(TokenFromRid(1, mdtAssembly), NULL, NULL, NULL, NULL, NULL, pdwFlags); -} - -// ------------------------------------------------------------ -// Metadata access -// ------------------------------------------------------------ - -inline void PEFile::OpenMDImport() -{ - WRAPPER_NO_CONTRACT; - //need synchronization - _ASSERTE(m_pMetadataLock->LockTaken() && m_pMetadataLock->IsWriterLock()); - OpenMDImport_Unsafe(); -} - -inline PEFile* PEFile::Dummy() -{ - return (PEFile*)(-1); -} -#endif // PEFILE_INL_ +#endif // PEASSEMBLY_INL_ diff --git a/src/coreclr/vm/peimage.cpp b/src/coreclr/vm/peimage.cpp index 412d67c938f5e..584797e16279b 100644 --- a/src/coreclr/vm/peimage.cpp +++ b/src/coreclr/vm/peimage.cpp @@ -61,44 +61,20 @@ CHECK PEImage::CheckStartup() CHECK_OK; } -/* static */ -CHECK PEImage::CheckLayoutFormat(PEDecoder *pe) -{ - CONTRACT_CHECK - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - INJECT_FAULT(COMPlusThrowOM();); - } - CONTRACT_CHECK_END; - - CHECK(pe->IsILOnly()); - CHECK_OK; -} - CHECK PEImage::CheckILFormat() { WRAPPER_NO_CONTRACT; - - PTR_PEImageLayout pLayoutToCheck; - PEImageLayoutHolder pLayoutHolder; - - if (HasLoadedLayout()) - { - pLayoutToCheck = GetLoadedLayout(); - } - else - { - pLayoutHolder = GetLayout(PEImageLayout::LAYOUT_ANY,LAYOUT_CREATEIFNEEDED); - pLayoutToCheck = pLayoutHolder; - } - - CHECK(pLayoutToCheck->CheckILFormat()); - + CHECK(GetOrCreateLayout(PEImageLayout::LAYOUT_ANY)->CheckILFormat()); CHECK_OK; }; +// PEImage is always unique on CoreCLR so a simple pointer check is sufficient in PEImage::Equals +CHECK PEImage::CheckUniqueInstance() +{ + CHECK(GetPath().IsEmpty() || m_bInHashMap); + CHECK_OK; +} + PEImage::~PEImage() { CONTRACTL @@ -116,7 +92,7 @@ PEImage::~PEImage() if (m_pLayoutLock) delete m_pLayoutLock; - if(m_hFile!=INVALID_HANDLE_VALUE && m_bOwnHandle) + if(m_hFile!=INVALID_HANDLE_VALUE) CloseHandle(m_hFile); for (unsigned int i=0;iDeleteValue(GetIDHash(), &locator); + PEImage* deleted = (PEImage *)s_Images->DeleteValue(GetPathHash(), &locator); _ASSERTE(deleted == this); } } @@ -252,44 +228,6 @@ CHECK PEImage::CheckCanonicalFullPath(const SString &path) CHECK_OK; } -BOOL PEImage::PathEquals(const SString &p1, const SString &p2) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - -#ifdef FEATURE_CASE_SENSITIVE_FILESYSTEM - return p1.Equals(p2); -#else - return p1.EqualsCaseInsensitive(p2); -#endif -} - -#ifndef TARGET_UNIX -/* static */ -void PEImage::GetPathFromDll(HINSTANCE hMod, SString &result) -{ - CONTRACTL - { - PRECONDITION(CheckStartup()); - PRECONDITION(CheckPointer(hMod)); - PRECONDITION(CheckValue(result)); - THROWS; - GC_NOTRIGGER; - MODE_ANY; - INJECT_FAULT(COMPlusThrowOM();); - } - CONTRACTL_END; - - WszGetModuleFileName(hMod, result); - -} -#endif // !TARGET_UNIX - /* static */ BOOL PEImage::CompareImage(UPTR u1, UPTR u2) { @@ -307,16 +245,25 @@ BOOL PEImage::CompareImage(UPTR u1, UPTR u2) // This is the value stored in the table PEImage *pImage = (PEImage *) u2; + if (pLocator->m_bIsInBundle != pImage->IsInBundle()) + { + return FALSE; + } BOOL ret = FALSE; HRESULT hr; EX_TRY { SString path(SString::Literal, pLocator->m_pPath); - BOOL isInBundle = pLocator->m_bIsInBundle; - if (PathEquals(path, pImage->GetPath()) && - (!isInBundle == !pImage->IsInBundle())) + +#ifdef FEATURE_CASE_SENSITIVE_FILESYSTEM + if (pImage->GetPath().Equals(path)) +#else + if (pImage->GetPath().EqualsCaseInsensitive(path)) +#endif + { ret = TRUE; + } } EX_CATCH_HRESULT(hr); //ignores failure! return ret; @@ -335,8 +282,8 @@ BOOL PEImage::Equals(PEImage *pImage) CONTRACTL_END; // PEImage is always unique on CoreCLR so a simple pointer check is sufficient - _ASSERTE(m_bInHashMap || GetPath().IsEmpty()); - _ASSERTE(pImage->m_bInHashMap || pImage->GetPath().IsEmpty()); + _ASSERTE(CheckUniqueInstance()); + _ASSERTE(pImage->CheckUniqueInstance()); return dac_cast(pImage) == dac_cast(this); } @@ -791,18 +738,16 @@ void PEImage::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) PEImage::PEImage(): m_path(), m_refCount(1), - m_bundleFileLocation(), m_bInHashMap(FALSE), + m_bundleFileLocation(), + m_hFile(INVALID_HANDLE_VALUE), + m_dwPEKind(0), + m_dwMachine(0), #ifdef METADATATRACKER_DATA m_pMDTracker(NULL), #endif // METADATATRACKER_DATA m_pMDImport(NULL), - m_pNativeMDImport(NULL), - m_hFile(INVALID_HANDLE_VALUE), - m_bOwnHandle(true), - m_dwPEKind(0), - m_dwMachine(0), - m_fCachedKindAndMachine(FALSE) + m_pNativeMDImport(NULL) { CONTRACTL { @@ -816,48 +761,46 @@ PEImage::PEImage(): m_pLayoutLock=new SimpleRWLock(PREEMPTIVE,LOCK_TYPE_DEFAULT); } -PTR_PEImageLayout PEImage::GetLayout(DWORD imageLayoutMask,DWORD flags) +PTR_PEImageLayout PEImage::GetOrCreateLayout(DWORD imageLayoutMask) { WRAPPER_NO_CONTRACT; SUPPORTS_DAC; - PTR_PEImageLayout pRetVal; + // First attempt to find an existing layout matching imageLayoutMask. + // If that fails, try again with auto-creating helper. + // Note: we use reader-writer lock, but only writes are synchronized. + PTR_PEImageLayout pRetVal = GetExistingLayoutInternal(imageLayoutMask); #ifndef DACCESS_COMPILE - // First attempt to find an existing layout matching imageLayoutMask. If that fails, - // and the caller has asked us to create layouts if needed, then try again passing - // the create flag to GetLayoutInternal. We need this to be synchronized, but the common - // case is that the layout already exists, so use a reader-writer lock. - GCX_PREEMP(); - { - SimpleReadLockHolder lock(m_pLayoutLock); - pRetVal=GetLayoutInternal(imageLayoutMask,flags&(~LAYOUT_CREATEIFNEEDED)); - } - - if (!(pRetVal || (flags&LAYOUT_CREATEIFNEEDED)==0)) + if (pRetVal == NULL) { + GCX_PREEMP(); SimpleWriteLockHolder lock(m_pLayoutLock); - pRetVal = GetLayoutInternal(imageLayoutMask,flags); + pRetVal = GetOrCreateLayoutInternal(imageLayoutMask); } - - return pRetVal; - #else // In DAC builds, we can't create any layouts - we must require that they already exist. // We also don't take any AddRefs or locks in DAC builds - it's inspection-only. - pRetVal = GetExistingLayoutInternal(imageLayoutMask); - if ((pRetVal==NULL) && (flags & LAYOUT_CREATEIFNEEDED)) - { - _ASSERTE_MSG(false, "DACization error - caller expects PEImage layout to exist and it doesn't"); - DacError(E_UNEXPECTED); - } - return pRetVal; + _ASSERTE_MSG(false, "DACization error - caller expects PEImage layout to exist and it doesn't"); + DacError(E_UNEXPECTED); #endif + + return pRetVal; } #ifndef DACCESS_COMPILE -PTR_PEImageLayout PEImage::GetLayoutInternal(DWORD imageLayoutMask,DWORD flags) +void PEImage::SetLayout(DWORD dwLayout, PEImageLayout* pLayout) +{ + LIMITED_METHOD_CONTRACT; + _ASSERTE(dwLayout < IMAGE_COUNT); + _ASSERTE(m_pLayoutLock->IsWriterLock()); + _ASSERTE(m_pLayouts[dwLayout] == NULL); + + m_pLayouts[dwLayout] = pLayout; +} + +PTR_PEImageLayout PEImage::GetOrCreateLayoutInternal(DWORD imageLayoutMask) { CONTRACTL { @@ -869,9 +812,9 @@ PTR_PEImageLayout PEImage::GetLayoutInternal(DWORD imageLayoutMask,DWORD flags) PTR_PEImageLayout pRetVal=GetExistingLayoutInternal(imageLayoutMask); - if (pRetVal==NULL && (flags&LAYOUT_CREATEIFNEEDED)) + if (pRetVal==NULL) { - _ASSERTE(HasID()); + _ASSERTE(HasPath()); BOOL bIsMappedLayoutSuitable = ((imageLayoutMask & PEImageLayout::LAYOUT_MAPPED) != 0); BOOL bIsFlatLayoutSuitable = ((imageLayoutMask & PEImageLayout::LAYOUT_FLAT) != 0); @@ -906,11 +849,8 @@ PTR_PEImageLayout PEImage::GetLayoutInternal(DWORD imageLayoutMask,DWORD flags) } } - if (pRetVal != NULL) - { - pRetVal->AddRef(); - } - + _ASSERTE(pRetVal != NULL); + _ASSERTE(this->IsOpened()); return pRetVal; } @@ -982,9 +922,10 @@ PTR_PEImageLayout PEImage::CreateLayoutMapped() } else { - PEImageLayoutHolder flatPE(GetLayoutInternal(PEImageLayout::LAYOUT_FLAT,LAYOUT_CREATEIFNEEDED)); + PEImageLayout* flatPE = GetOrCreateLayout(PEImageLayout::LAYOUT_FLAT); if (!flatPE->CheckFormat() || !flatPE->IsILOnly()) ThrowHR(COR_E_BADIMAGEFORMAT); + pRetVal=PEImageLayout::LoadFromFlat(flatPE); SetLayout(IMAGE_MAPPED,pRetVal); } @@ -1034,6 +975,10 @@ PTR_PEImage PEImage::LoadFlat(const void *flat, COUNT_T size) PEImageHolder pImage(new PEImage()); PTR_PEImageLayout pLayout = PEImageLayout::CreateFlat(flat,size,pImage); _ASSERTE(!pLayout->IsMapped()); + + //Not taking a lock here since we have just created pImage + //SimpleWriteLockHolder lock(pImage->m_pLayoutLock); + pImage->SetLayout(IMAGE_FLAT,pLayout); RETURN dac_cast(pImage.Extract()); } @@ -1051,12 +996,13 @@ PTR_PEImage PEImage::LoadImage(HMODULE hMod) CONTRACT_END; StackSString path; - GetPathFromDll(hMod, path); - PEImageHolder pImage(PEImage::OpenImage(path,(MDInternalImportFlags)(0))); + WszGetModuleFileName(hMod, path); + PEImageHolder pImage(PEImage::OpenImage(path, MDInternalImport_Default)); if (pImage->HasLoadedLayout()) RETURN dac_cast(pImage.Extract()); - SimpleWriteLockHolder lock(pImage->m_pLayoutLock); + //Not taking a lock here since we have just created pImage + // SimpleWriteLockHolder lock(pImage->m_pLayoutLock); if(pImage->m_pLayouts[IMAGE_LOADED]==NULL) pImage->SetLayout(IMAGE_LOADED,PEImageLayout::CreateFromHMODULE(hMod,pImage,WszGetModuleHandle(NULL)!=hMod)); @@ -1134,18 +1080,6 @@ void PEImage::Load() } } -void PEImage::SetLoadedHMODULE(HMODULE hMod) -{ - WRAPPER_NO_CONTRACT; - SimpleWriteLockHolder lock(m_pLayoutLock); - if(m_pLayouts[IMAGE_LOADED]) - { - _ASSERTE(m_pLayouts[IMAGE_LOADED]->GetBase()==hMod); - return; - } - SetLayout(IMAGE_LOADED,PEImageLayout::CreateFromHMODULE(hMod,this,TRUE)); -} - void PEImage::LoadFromMapped() { STANDARD_VM_CONTRACT; @@ -1156,10 +1090,13 @@ void PEImage::LoadFromMapped() return; } - PEImageLayoutHolder pLayout(GetLayout(PEImageLayout::LAYOUT_MAPPED,LAYOUT_CREATEIFNEEDED)); + PEImageLayout* pLayout = GetOrCreateLayout(PEImageLayout::LAYOUT_MAPPED); SimpleWriteLockHolder lock(m_pLayoutLock); - if(m_pLayouts[IMAGE_LOADED]==NULL) - SetLayout(IMAGE_LOADED,pLayout.Extract()); + if (m_pLayouts[IMAGE_LOADED] == NULL) + { + pLayout->AddRef(); + SetLayout(IMAGE_LOADED, pLayout); + } } void PEImage::LoadNoFile() @@ -1173,38 +1110,18 @@ void PEImage::LoadNoFile() if (HasLoadedLayout()) return; - PEImageLayoutHolder pLayout(GetLayout(PEImageLayout::LAYOUT_ANY,0)); + PEImageLayout* pLayout = GetExistingLayoutInternal(PEImageLayout::LAYOUT_ANY); if (!pLayout->CheckILOnly()) ThrowHR(COR_E_BADIMAGEFORMAT); - SimpleWriteLockHolder lock(m_pLayoutLock); - if(m_pLayouts[IMAGE_LOADED]==NULL) - SetLayout(IMAGE_LOADED,pLayout.Extract()); -} - - -void PEImage::LoadNoMetaData() -{ - STANDARD_VM_CONTRACT; - - if (HasLoadedLayout()) - return; SimpleWriteLockHolder lock(m_pLayoutLock); - if (m_pLayouts[IMAGE_LOADED]!=NULL) - return; - if (m_pLayouts[IMAGE_FLAT]!=NULL) - { - m_pLayouts[IMAGE_FLAT]->AddRef(); - SetLayout(IMAGE_LOADED,m_pLayouts[IMAGE_FLAT]); - } - else + if (m_pLayouts[IMAGE_LOADED] == NULL) { - _ASSERTE(!m_path.IsEmpty()); - SetLayout(IMAGE_LOADED,PEImageLayout::LoadFlat(this)); + pLayout->AddRef(); + SetLayout(IMAGE_LOADED, pLayout); } } - #endif //DACCESS_COMPILE //------------------------------------------------------------------------------- @@ -1264,22 +1181,6 @@ HANDLE PEImage::GetFileHandle() return m_hFile; } -void PEImage::SetFileHandle(HANDLE hFile) -{ - CONTRACTL - { - STANDARD_VM_CHECK; - } - CONTRACTL_END; - - SimpleWriteLockHolder lock(m_pLayoutLock); - if (m_hFile == INVALID_HANDLE_VALUE) - { - m_hFile = hFile; - m_bOwnHandle = false; - } -} - HRESULT PEImage::TryOpenFile() { STANDARD_VM_CONTRACT; diff --git a/src/coreclr/vm/peimage.h b/src/coreclr/vm/peimage.h index af86e35ccaec4..679b3e024d337 100644 --- a/src/coreclr/vm/peimage.h +++ b/src/coreclr/vm/peimage.h @@ -26,9 +26,6 @@ class SimpleRWLock; // -------------------------------------------------------------------------------- class Crst; -class Thread; - -Thread* GetThreadNULLOk(); // -------------------------------------------------------------------------------- // PEImage is a PE file loaded by our "simulated LoadLibrary" mechanism. A PEImage @@ -56,92 +53,54 @@ struct CV_INFO_PDB70 typedef DPTR(class PEImage) PTR_PEImage; -class PEImage +class PEImage final { -public: - // ------------------------------------------------------------ - // Public constants - // ------------------------------------------------------------ - - enum - { - LAYOUT_CREATEIFNEEDED=1 - }; - PTR_PEImageLayout GetLayout(DWORD imageLayoutMask,DWORD flags); //with ref - PTR_PEImageLayout GetLoadedLayout(); //no ref - BOOL IsOpened(); - BOOL HasLoadedLayout(); public: // ------------------------------------------------------------ // Public API // ------------------------------------------------------------ + // initialize static data (i.e. locks, unique instance cache, etc..) static void Startup(); - // Normal constructed PEImages do NOT share images between calls and - // cannot be accessed by Get methods. - // - // DO NOT USE these unless you want a private copy-on-write mapping of - // the file. - - - -public: ~PEImage(); PEImage(); + BOOL Equals(PEImage* pImage); + + ULONG AddRef(); + ULONG Release(); + #ifndef DACCESS_COMPILE - static PTR_PEImage LoadFlat( - const void *flat, - COUNT_T size); + static PTR_PEImage LoadFlat(const void *flat, COUNT_T size); #ifndef TARGET_UNIX - static PTR_PEImage LoadImage( - HMODULE hMod); + static PTR_PEImage LoadImage(HMODULE hMod); #endif // !TARGET_UNIX static PTR_PEImage OpenImage( LPCWSTR pPath, MDInternalImportFlags flags = MDInternalImport_Default, BundleFileLocation bundleFileLocation = BundleFileLocation::Invalid()); - - // clones the image with new flags (this is pretty much about cached / noncached difference) - void Clone(MDInternalImportFlags flags, PTR_PEImage* ppImage) - { - if (GetPath().IsEmpty()) - { - AddRef(); - *ppImage = this; - } - else - *ppImage = PEImage::OpenImage(GetPath(), flags); - - }; - - static PTR_PEImage FindById(UINT64 uStreamAsmId, DWORD dwModuleId); - static PTR_PEImage FindByPath(LPCWSTR pPath, - BOOL isInBundle = TRUE); - static PTR_PEImage FindByShortPath(LPCWSTR pPath); - static PTR_PEImage FindByLongPath(LPCWSTR pPath); + static PTR_PEImage FindByPath(LPCWSTR pPath, BOOL isInBundle = TRUE); void AddToHashMap(); void Load(); - void SetLoadedHMODULE(HMODULE hMod); - void LoadNoMetaData(); void LoadNoFile(); void LoadFromMapped(); #endif - BOOL HasID(); - ULONG GetIDHash(); + BOOL IsOpened(); + PTR_PEImageLayout GetOrCreateLayout(DWORD imageLayoutMask); - // Refcount above images. - ULONG AddRef(); - ULONG Release(); + BOOL HasLoadedLayout(); + PTR_PEImageLayout GetLoadedLayout(); - // Accessors - const SString &GetPath(); + BOOL HasPath(); + ULONG GetPathHash(); + const SString& GetPath(); const SString& GetPathToLoad(); + BOOL IsFile(); BOOL IsInBundle() const; HANDLE GetFileHandle(); @@ -149,14 +108,10 @@ class PEImage INT64 GetSize() const; INT64 GetUncompressedSize() const; - void SetFileHandle(HANDLE hFile); HRESULT TryOpenFile(); LPCWSTR GetPathForErrorMessages(); - // Equality - BOOL Equals(PEImage *pImage); - void GetMVID(GUID *pMvid); BOOL HasV1Metadata(); IMDInternalImport* GetMDImport(); @@ -165,54 +120,53 @@ class PEImage BOOL HasContents() ; BOOL IsPtrInImage(PTR_CVOID data); - CHECK CheckFormat(); - - // Check utilites - CHECK CheckILFormat(); - static CHECK CheckCanonicalFullPath(const SString &path); - static CHECK CheckStartup(); - PTR_CVOID GetMetadata(COUNT_T *pSize = NULL); - -#ifndef TARGET_UNIX - static void GetPathFromDll(HINSTANCE hMod, SString &result); -#endif // !TARGET_UNIX - static BOOL PathEquals(const SString &p1, const SString &p2); - - void SetModuleFileNameHintForDAC(); -#ifdef DACCESS_COMPILE - void EnumMemoryRegions(CLRDataEnumMemoryFlags flags); - const SString &GetModuleFileNameHintForDAC(); -#endif BOOL HasNTHeaders(); BOOL HasCorHeader(); BOOL HasReadyToRunHeader(); + BOOL HasDirectoryEntry(int entry); + BOOL Has32BitNTHeaders(); + + void GetPEKindAndMachine(DWORD* pdwKind, DWORD* pdwMachine); + + BOOL IsILOnly(); BOOL IsReferenceAssembly(); BOOL IsComponentAssembly(); - PTR_CVOID GetNativeManifestMetadata(COUNT_T *pSize = NULL); - BOOL HasDirectoryEntry(int entry); + + PTR_CVOID GetNativeManifestMetadata(COUNT_T* pSize = NULL); mdToken GetEntryPointToken(); DWORD GetCorHeaderFlags(); - BOOL IsILOnly(); - BOOL IsDll(); - WORD GetSubsystem(); - BOOL IsFileLocked(); - BOOL IsIbcOptimized(); - BOOL Has32BitNTHeaders(); + PTR_CVOID GetMetadata(COUNT_T* pSize = NULL); + + // Check utilites + static CHECK CheckStartup(); + static CHECK CheckCanonicalFullPath(const SString& path); + + CHECK CheckFormat(); + CHECK CheckILFormat(); + CHECK CheckUniqueInstance(); void VerifyIsAssembly(); + void SetModuleFileNameHintForDAC(); +#ifdef DACCESS_COMPILE + void EnumMemoryRegions(CLRDataEnumMemoryFlags flags); + const SString &GetModuleFileNameHintForDAC(); +#endif + private: #ifndef DACCESS_COMPILE // Get or create the layout corresponding to the mask, with an AddRef - PTR_PEImageLayout GetLayoutInternal(DWORD imageLayoutMask, DWORD flags); + PTR_PEImageLayout GetOrCreateLayoutInternal(DWORD imageLayoutMask); // Create the mapped layout PTR_PEImageLayout CreateLayoutMapped(); // Create the flat layout PTR_PEImageLayout CreateLayoutFlat(BOOL bPermitWriteableSections); + + void SetLayout(DWORD dwLayout, PTR_PEImageLayout pLayout); #endif // Get an existing layout corresponding to the mask, no AddRef PTR_PEImageLayout GetExistingLayoutInternal(DWORD imageLayoutMask); @@ -249,28 +203,83 @@ class PEImage void DECLSPEC_NORETURN ThrowFormat(HRESULT hr); - static CHECK CheckLayoutFormat(PEDecoder *pe); +public: + class IJWFixupData + { + private: + Crst m_lock; + void* m_base; + DWORD m_flags; + PTR_LoaderHeap m_DllThunkHeap; + + // the fixup for the next iteration in FixupVTables + // we use it to make sure that we do not try to fix up the same entry twice + // if there was a pass that was aborted in the middle + COUNT_T m_iNextFixup; + COUNT_T m_iNextMethod; + + enum { + e_FIXED_UP = 0x1 + }; + + public: + IJWFixupData(void* pBase); + ~IJWFixupData(); + void* GetBase() { LIMITED_METHOD_CONTRACT; return m_base; } + Crst* GetLock() { LIMITED_METHOD_CONTRACT; return &m_lock; } + BOOL IsFixedUp() { LIMITED_METHOD_CONTRACT; return m_flags & e_FIXED_UP; } + void SetIsFixedUp() { LIMITED_METHOD_CONTRACT; m_flags |= e_FIXED_UP; } + PTR_LoaderHeap GetThunkHeap(); + void MarkMethodFixedUp(COUNT_T iFixup, COUNT_T iMethod); + BOOL IsMethodFixedUp(COUNT_T iFixup, COUNT_T iMethod); + }; + + static IJWFixupData* GetIJWData(void* pBase); + static PTR_LoaderHeap GetDllThunkHeap(void* pBase); + static void UnloadIJWModule(void* pBase); + +private: // ------------------------------------------------------------ - // Instance members + // Static fields // ------------------------------------------------------------ - SString m_path; - LONG m_refCount; + static CrstStatic s_hashLock; + static PtrHashMap* s_Images; - BundleFileLocation m_bundleFileLocation; // If this image is located within a single-file bundle, - // the location within the bundle. If m_bundleFileLocation is valid, - // it takes precedence over m_path for loading. +//@TODO:workaround: Remove this when we have one PEImage per mapped image, +//@TODO:workaround: and move the lock there +// This is for IJW thunk initialization, as it is no longer guaranteed +// that the initialization will occur under the loader lock. + static CrstStatic s_ijwHashLock; + static PtrHashMap* s_ijwFixupDataHash; + + // ------------------------------------------------------------ + // Instance fields + // ------------------------------------------------------------ + + SString m_path; + LONG m_refCount; + + // means this is a unique (deduped) instance. + BOOL m_bInHashMap; + + // If this image is located within a single-file bundle, the location within the bundle. + // If m_bundleFileLocation is valid, it takes precedence over m_path for loading. + BundleFileLocation m_bundleFileLocation; + + // valid handle if we tried to open the file/path and succeeded. + HANDLE m_hFile; + + DWORD m_dwPEKind; + DWORD m_dwMachine; // This variable will have the data of module name. // It is only used by DAC to remap fusion loaded modules back to // disk IL. This really is a workaround. The real fix is for fusion loader // hook (public API on hosting) to take an additional file name hint. // We are piggy backing on the fact that module name is the same as file name!!! - // - SString m_sModuleFileNameHintUsedByDac; // This is only used by DAC - -protected: + SString m_sModuleFileNameHintUsedByDac; // This is only used by DAC enum { @@ -282,11 +291,6 @@ class PEImage SimpleRWLock *m_pLayoutLock; PTR_PEImageLayout m_pLayouts[IMAGE_COUNT] ; - BOOL m_bInHashMap; -#ifndef DACCESS_COMPILE - void SetLayout(DWORD dwLayout, PTR_PEImageLayout pLayout); -#endif // DACCESS_COMPILE - #ifdef METADATATRACKER_DATA class MetaDataTracker *m_pMDTracker; @@ -294,75 +298,6 @@ class PEImage IMDInternalImport* m_pMDImport; IMDInternalImport* m_pNativeMDImport; - - -private: - - - // ------------------------------------------------------------ - // Static members - // ------------------------------------------------------------ - - static CrstStatic s_hashLock; - - static PtrHashMap *s_Images; - - HANDLE m_hFile; - bool m_bOwnHandle; - - //@TODO:workaround: Remove this when we have one PEImage per mapped image, - //@TODO:workaround: and move the lock there - // This is for IJW thunk initialization, as it is no longer guaranteed - // that the initialization will occur under the loader lock. - static CrstStatic s_ijwHashLock; - static PtrHashMap *s_ijwFixupDataHash; - -public: - class IJWFixupData - { - private: - Crst m_lock; - void *m_base; - DWORD m_flags; - PTR_LoaderHeap m_DllThunkHeap; - - // the fixup for the next iteration in FixupVTables - // we use it to make sure that we do not try to fix up the same entry twice - // if there was a pass that was aborted in the middle - COUNT_T m_iNextFixup; - COUNT_T m_iNextMethod; - - enum { - e_FIXED_UP = 0x1 - }; - - public: - IJWFixupData(void *pBase); - ~IJWFixupData(); - void *GetBase() { LIMITED_METHOD_CONTRACT; return m_base; } - Crst *GetLock() { LIMITED_METHOD_CONTRACT; return &m_lock; } - BOOL IsFixedUp() { LIMITED_METHOD_CONTRACT; return m_flags & e_FIXED_UP; } - void SetIsFixedUp() { LIMITED_METHOD_CONTRACT; m_flags |= e_FIXED_UP; } - PTR_LoaderHeap GetThunkHeap(); - void MarkMethodFixedUp(COUNT_T iFixup, COUNT_T iMethod); - BOOL IsMethodFixedUp(COUNT_T iFixup, COUNT_T iMethod); - }; - - static IJWFixupData *GetIJWData(void *pBase); - static PTR_LoaderHeap GetDllThunkHeap(void *pBase); - static void UnloadIJWModule(void *pBase); - -private: - DWORD m_dwPEKind; - DWORD m_dwMachine; - BOOL m_fCachedKindAndMachine; - - - -public: - void CachePEKindAndMachine(); - void GetPEKindAndMachine(DWORD* pdwKind, DWORD* pdwMachine); - }; FORCEINLINE void PEImageRelease(PEImage *i) diff --git a/src/coreclr/vm/peimage.inl b/src/coreclr/vm/peimage.inl index b855d379bd31e..8dc72d416fac3 100644 --- a/src/coreclr/vm/peimage.inl +++ b/src/coreclr/vm/peimage.inl @@ -106,24 +106,6 @@ inline BOOL PEImage::IsFile() return !GetPathToLoad().IsEmpty(); } -#ifndef DACCESS_COMPILE -inline void PEImage::SetLayout(DWORD dwLayout, PEImageLayout* pLayout) -{ - LIMITED_METHOD_CONTRACT; - _ASSERTE(dwLayoutHasNTHeaders(); - else - { - PEImageLayoutHolder pLayout(GetLayout(PEImageLayout::LAYOUT_ANY,LAYOUT_CREATEIFNEEDED)); - return pLayout->HasNTHeaders(); - } + return GetOrCreateLayout(PEImageLayout::LAYOUT_ANY)->HasNTHeaders(); } inline BOOL PEImage::HasCorHeader() { WRAPPER_NO_CONTRACT; - if (HasLoadedLayout()) - return GetLoadedLayout()->HasCorHeader(); - else - { - PEImageLayoutHolder pLayout(GetLayout(PEImageLayout::LAYOUT_ANY,LAYOUT_CREATEIFNEEDED)); - return pLayout->HasCorHeader(); - } + return GetOrCreateLayout(PEImageLayout::LAYOUT_ANY)->HasCorHeader(); } inline BOOL PEImage::IsComponentAssembly() { WRAPPER_NO_CONTRACT; - if (HasLoadedLayout()) - return GetLoadedLayout()->IsComponentAssembly(); - else - { - PEImageLayoutHolder pLayout(GetLayout(PEImageLayout::LAYOUT_ANY,LAYOUT_CREATEIFNEEDED)); - return pLayout->IsComponentAssembly(); - } + return GetOrCreateLayout(PEImageLayout::LAYOUT_ANY)->IsComponentAssembly(); } inline BOOL PEImage::HasReadyToRunHeader() { WRAPPER_NO_CONTRACT; - if (HasLoadedLayout()) - return GetLoadedLayout()->HasReadyToRunHeader(); - else - { - PEImageLayoutHolder pLayout(GetLayout(PEImageLayout::LAYOUT_ANY,LAYOUT_CREATEIFNEEDED)); - return pLayout->HasReadyToRunHeader(); - } + return GetOrCreateLayout(PEImageLayout::LAYOUT_ANY)->HasReadyToRunHeader(); } inline BOOL PEImage::HasDirectoryEntry(int entry) { WRAPPER_NO_CONTRACT; - if (HasLoadedLayout()) - return GetLoadedLayout()->HasDirectoryEntry(entry); - else - { - PEImageLayoutHolder pLayout(GetLayout(PEImageLayout::LAYOUT_ANY,LAYOUT_CREATEIFNEEDED)); - return pLayout->HasDirectoryEntry(entry); - } + return GetOrCreateLayout(PEImageLayout::LAYOUT_ANY)->HasDirectoryEntry(entry); } inline mdToken PEImage::GetEntryPointToken() { WRAPPER_NO_CONTRACT; - if (HasLoadedLayout()) - { - PTR_PEImageLayout pLayout = GetLoadedLayout(); - if (!pLayout->HasManagedEntryPoint()) - return mdTokenNil; - return pLayout->GetEntryPointToken(); - } - else - { - PEImageLayoutHolder pLayout(GetLayout(PEImageLayout::LAYOUT_ANY,LAYOUT_CREATEIFNEEDED)); - if (!pLayout->HasManagedEntryPoint()) - return mdTokenNil; - return pLayout->GetEntryPointToken(); - } + PEImageLayout* pLayout = GetOrCreateLayout(PEImageLayout::LAYOUT_ANY); + if (!pLayout->HasManagedEntryPoint()) + return mdTokenNil; + return pLayout->GetEntryPointToken(); } inline DWORD PEImage::GetCorHeaderFlags() { WRAPPER_NO_CONTRACT; - - if (HasLoadedLayout()) - { - PTR_PEImageLayout pLayout = GetLoadedLayout(); - return VAL32(pLayout->GetCorHeader()->Flags); - } - else - { - PEImageLayoutHolder pLayout(GetLayout(PEImageLayout::LAYOUT_ANY,LAYOUT_CREATEIFNEEDED)); - return VAL32(pLayout->GetCorHeader()->Flags); - } + return VAL32(GetOrCreateLayout(PEImageLayout::LAYOUT_ANY)->GetCorHeader()->Flags); } inline BOOL PEImage::MDImportLoaded() @@ -299,93 +240,32 @@ inline BOOL PEImage::HasV1Metadata() inline BOOL PEImage::IsILOnly() { WRAPPER_NO_CONTRACT; - if (HasLoadedLayout()) - return GetLoadedLayout()->IsILOnly(); - else - { - PEImageLayoutHolder pLayout(GetLayout(PEImageLayout::LAYOUT_ANY,LAYOUT_CREATEIFNEEDED)); - return pLayout->IsILOnly(); - } -} - -inline WORD PEImage::GetSubsystem() -{ - WRAPPER_NO_CONTRACT; - SUPPORTS_DAC; - - if (HasLoadedLayout()) - return GetLoadedLayout()->GetSubsystem(); - else - { - PEImageLayoutHolder pLayout(GetLayout(PEImageLayout::LAYOUT_ANY,LAYOUT_CREATEIFNEEDED)); - return pLayout->GetSubsystem(); - } -} - -inline BOOL PEImage::IsDll() -{ - WRAPPER_NO_CONTRACT; - if (HasLoadedLayout()) - return GetLoadedLayout()->IsDll(); - else - { - PEImageLayoutHolder pLayout(GetLayout(PEImageLayout::LAYOUT_ANY,LAYOUT_CREATEIFNEEDED)); - return pLayout->IsDll(); - } -} - -inline BOOL PEImage::IsIbcOptimized() -{ - return false; + return GetOrCreateLayout(PEImageLayout::LAYOUT_ANY)->IsILOnly(); } inline PTR_CVOID PEImage::GetNativeManifestMetadata(COUNT_T *pSize) { WRAPPER_NO_CONTRACT; - if (HasLoadedLayout()) - return GetLoadedLayout()->GetNativeManifestMetadata(pSize); - else - { - PEImageLayoutHolder pLayout(GetLayout(PEImageLayout::LAYOUT_ANY,LAYOUT_CREATEIFNEEDED)); - return pLayout->GetNativeManifestMetadata(pSize); - } + return GetOrCreateLayout(PEImageLayout::LAYOUT_ANY)->GetNativeManifestMetadata(pSize); } inline PTR_CVOID PEImage::GetMetadata(COUNT_T *pSize) { WRAPPER_NO_CONTRACT; - if (HasLoadedLayout()) - return GetLoadedLayout()->GetMetadata(pSize); - else - { - PEImageLayoutHolder pLayout(GetLayout(PEImageLayout::LAYOUT_ANY,LAYOUT_CREATEIFNEEDED)); - return pLayout->GetMetadata(pSize); - } + return GetOrCreateLayout(PEImageLayout::LAYOUT_ANY)->GetMetadata(pSize); } inline BOOL PEImage::HasContents() { WRAPPER_NO_CONTRACT; - if (HasLoadedLayout()) - return GetLoadedLayout()->HasContents(); - else - { - PEImageLayoutHolder pLayout(GetLayout(PEImageLayout::LAYOUT_ANY,LAYOUT_CREATEIFNEEDED)); - return pLayout->HasContents(); - } + return GetOrCreateLayout(PEImageLayout::LAYOUT_ANY)->HasContents(); } inline CHECK PEImage::CheckFormat() { WRAPPER_NO_CONTRACT; - if (HasLoadedLayout()) - CHECK(GetLoadedLayout()->CheckFormat()); - else - { - PEImageLayoutHolder pLayout(GetLayout(PEImageLayout::LAYOUT_ANY,LAYOUT_CREATEIFNEEDED)); - CHECK(pLayout->CheckFormat()); - } + CHECK(GetOrCreateLayout(PEImageLayout::LAYOUT_ANY)->CheckFormat()); CHECK_OK; } @@ -429,15 +309,13 @@ inline PTR_PEImage PEImage::FindByPath(LPCWSTR pPath, BOOL isInBundle /* = TRUE DWORD dwHash = CaseHashHelper(pPath, (COUNT_T) wcslen(pPath)); #endif return (PEImage *) s_Images->LookupValue(dwHash, &locator); - } /* static */ inline PTR_PEImage PEImage::OpenImage(LPCWSTR pPath, MDInternalImportFlags flags /* = MDInternalImport_Default */, BundleFileLocation bundleFileLocation) { - BOOL fUseCache = !((flags & MDInternalImport_NoCache) == MDInternalImport_NoCache); - - if (!fUseCache) + BOOL forbidCache = (flags & MDInternalImport_NoCache); + if (forbidCache) { PEImageHolder pImage(new PEImage); pImage->Init(pPath, bundleFileLocation); @@ -447,8 +325,6 @@ inline PTR_PEImage PEImage::OpenImage(LPCWSTR pPath, MDInternalImportFlags flags CrstHolder holder(&s_hashLock); PEImage* found = FindByPath(pPath, bundleFileLocation.IsValid()); - - if (found == (PEImage*) INVALIDENTRY) { // We did not find the entry in the Cache, and we've been asked to only use the cache. @@ -470,15 +346,8 @@ inline PTR_PEImage PEImage::OpenImage(LPCWSTR pPath, MDInternalImportFlags flags } #endif -inline BOOL PEImage::IsFileLocked() -{ - WRAPPER_NO_CONTRACT; - return (m_pLayouts[IMAGE_FLAT])!=NULL || (m_pLayouts[IMAGE_MAPPED])!=NULL ; -} - #ifndef DACCESS_COMPILE - inline void PEImage::AddToHashMap() { CONTRACTL @@ -490,40 +359,30 @@ inline void PEImage::AddToHashMap() CONTRACTL_END; _ASSERTE(s_hashLock.OwnedByCurrentThread()); - s_Images->InsertValue(GetIDHash(),this); + s_Images->InsertValue(GetPathHash(),this); m_bInHashMap=TRUE; } #endif - - - inline BOOL PEImage::Has32BitNTHeaders() { WRAPPER_NO_CONTRACT; - if (HasLoadedLayout()) - return GetLoadedLayout()->Has32BitNTHeaders(); - else - { - PEImageLayoutHolder pLayout(GetLayout(PEImageLayout::LAYOUT_ANY,LAYOUT_CREATEIFNEEDED)); - return pLayout->Has32BitNTHeaders(); - } + return GetOrCreateLayout(PEImageLayout::LAYOUT_ANY)->Has32BitNTHeaders(); } -inline BOOL PEImage::HasID() +inline BOOL PEImage::HasPath() { LIMITED_METHOD_CONTRACT; - return !GetPath().IsEmpty(); } -inline ULONG PEImage::GetIDHash() +inline ULONG PEImage::GetPathHash() { CONTRACT(ULONG) { - PRECONDITION(HasID()); + PRECONDITION(HasPath()); MODE_ANY; GC_NOTRIGGER; THROWS; @@ -537,7 +396,7 @@ inline ULONG PEImage::GetIDHash() #endif } -inline void PEImage::CachePEKindAndMachine() +inline void PEImage::GetPEKindAndMachine(DWORD* pdwKind, DWORD* pdwMachine) { CONTRACTL { @@ -547,40 +406,20 @@ inline void PEImage::CachePEKindAndMachine() } CONTRACTL_END; - // Do nothing if we have cached the information already - if(m_fCachedKindAndMachine) - return; - - PEImageLayoutHolder pLayout; - if (HasLoadedLayout()) - { - pLayout.Assign(GetLoadedLayout(), false); - } - else + // first check if we have a valid PE kind + if (VolatileLoad(&m_dwPEKind) == 0) { - pLayout.Assign(GetLayout(PEImageLayout::LAYOUT_MAPPED|PEImageLayout::LAYOUT_FLAT, - PEImage::LAYOUT_CREATEIFNEEDED)); - } - - // Compute result into a local variables first - DWORD dwPEKind, dwMachine; - pLayout->GetPEKindAndMachine(&dwPEKind, &dwMachine); + // Compute result into a local variables first + DWORD dwPEKind, dwMachine; + GetOrCreateLayout(PEImageLayout::LAYOUT_ANY)->GetPEKindAndMachine(&dwPEKind, &dwMachine); - // Write the final result into the lock-free cache. - m_dwPEKind = dwPEKind; - m_dwMachine = dwMachine; - MemoryBarrier(); - m_fCachedKindAndMachine = TRUE; -} + // Write the final results - first machine, then kind. + m_dwMachine = dwMachine; + VolatileStore(&m_dwPEKind, dwPEKind); + } -inline void PEImage::GetPEKindAndMachine(DWORD* pdwKind, DWORD* pdwMachine) -{ - WRAPPER_NO_CONTRACT; - CachePEKindAndMachine(); - if (pdwKind) - *pdwKind = m_dwPEKind; - if (pdwMachine) - *pdwMachine = m_dwMachine; + *pdwKind = m_dwPEKind; + *pdwMachine = m_dwMachine; } #endif // PEIMAGE_INL_ diff --git a/src/coreclr/vm/peimagelayout.cpp b/src/coreclr/vm/peimagelayout.cpp index 33081b9b60586..e4c4c274cf309 100644 --- a/src/coreclr/vm/peimagelayout.cpp +++ b/src/coreclr/vm/peimagelayout.cpp @@ -806,7 +806,7 @@ PEImageLayout::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) { WRAPPER_NO_CONTRACT; DAC_ENUM_VTHIS(); - EMEM_OUT(("MEM: %p PEFile\n", dac_cast(this))); + EMEM_OUT(("MEM: %p PEAssembly\n", dac_cast(this))); PEDecoder::EnumMemoryRegions(flags,false); } #endif //DACCESS_COMPILE diff --git a/src/coreclr/vm/perfinfo.cpp b/src/coreclr/vm/perfinfo.cpp index b645052f46d2e..667223fedc605 100644 --- a/src/coreclr/vm/perfinfo.cpp +++ b/src/coreclr/vm/perfinfo.cpp @@ -27,28 +27,28 @@ PerfInfo::PerfInfo(int pid) } // Logs image loads into the process' perfinfo-%d.map file -void PerfInfo::LogImage(PEFile* pFile, WCHAR* guid) +void PerfInfo::LogImage(PEAssembly* pPEAssembly, WCHAR* guid) { CONTRACTL { THROWS; GC_NOTRIGGER; MODE_PREEMPTIVE; - PRECONDITION(pFile != nullptr); + PRECONDITION(pPEAssembly != nullptr); PRECONDITION(guid != nullptr); } CONTRACTL_END; SString value; - const SString& path = pFile->GetPath(); + const SString& path = pPEAssembly->GetPath(); if (path.IsEmpty()) { return; } SIZE_T baseAddr = 0; - if (pFile->IsReadyToRun()) + if (pPEAssembly->IsReadyToRun()) { - PEImageLayout *pLoadedLayout = pFile->GetLoaded(); + PEImageLayout *pLoadedLayout = pPEAssembly->GetLoadedLayout(); if (pLoadedLayout) { baseAddr = (SIZE_T)pLoadedLayout->GetBase(); diff --git a/src/coreclr/vm/perfinfo.h b/src/coreclr/vm/perfinfo.h index 759a844f30925..16b06865925c6 100644 --- a/src/coreclr/vm/perfinfo.h +++ b/src/coreclr/vm/perfinfo.h @@ -22,7 +22,7 @@ class PerfInfo { public: PerfInfo(int pid); ~PerfInfo(); - void LogImage(PEFile* pFile, WCHAR* guid); + void LogImage(PEAssembly* pPEAssembly, WCHAR* guid); private: CFileStream* m_Stream; diff --git a/src/coreclr/vm/perfmap.cpp b/src/coreclr/vm/perfmap.cpp index 31da16cac5586..48f178aeb67c3 100644 --- a/src/coreclr/vm/perfmap.cpp +++ b/src/coreclr/vm/perfmap.cpp @@ -219,22 +219,22 @@ void PerfMap::LogMethod(MethodDesc * pMethod, PCODE pCode, size_t codeSize, cons } -void PerfMap::LogImageLoad(PEFile * pFile) +void PerfMap::LogImageLoad(PEAssembly * pPEAssembly) { if (s_enabled) { - s_Current->LogImage(pFile); + s_Current->LogImage(pPEAssembly); } } // Log an image load to the map. -void PerfMap::LogImage(PEFile * pFile) +void PerfMap::LogImage(PEAssembly * pPEAssembly) { CONTRACTL{ THROWS; GC_NOTRIGGER; MODE_PREEMPTIVE; - PRECONDITION(pFile != nullptr); + PRECONDITION(pPEAssembly != nullptr); } CONTRACTL_END; @@ -247,9 +247,9 @@ void PerfMap::LogImage(PEFile * pFile) EX_TRY { WCHAR wszSignature[39]; - GetNativeImageSignature(pFile, wszSignature, lengthof(wszSignature)); + GetNativeImageSignature(pPEAssembly, wszSignature, lengthof(wszSignature)); - m_PerfInfo->LogImage(pFile, wszSignature); + m_PerfInfo->LogImage(pPEAssembly, wszSignature); } EX_CATCH{} EX_END_CATCH(SwallowAllExceptions); } @@ -361,10 +361,10 @@ void PerfMap::LogStubs(const char* stubType, const char* stubOwner, PCODE pCode, EX_CATCH{} EX_END_CATCH(SwallowAllExceptions); } -void PerfMap::GetNativeImageSignature(PEFile * pFile, WCHAR * pwszSig, unsigned int nSigSize) +void PerfMap::GetNativeImageSignature(PEAssembly * pPEAssembly, WCHAR * pwszSig, unsigned int nSigSize) { CONTRACTL{ - PRECONDITION(pFile != nullptr); + PRECONDITION(pPEAssembly != nullptr); PRECONDITION(pwszSig != nullptr); PRECONDITION(nSigSize >= 39); } CONTRACTL_END; @@ -372,7 +372,7 @@ void PerfMap::GetNativeImageSignature(PEFile * pFile, WCHAR * pwszSig, unsigned // We use the MVID as the signature, since ready to run images // don't have a native image signature. GUID mvid; - pFile->GetMVID(&mvid); + pPEAssembly->GetMVID(&mvid); if(!StringFromGUID2(mvid, pwszSig, nSigSize)) { pwszSig[0] = '\0'; @@ -417,7 +417,7 @@ void NativeImagePerfMap::LogDataForModule(Module * pModule) { STANDARD_VM_CONTRACT; - PEImageLayout * pLoadedLayout = pModule->GetFile()->GetLoaded(); + PEImageLayout * pLoadedLayout = pModule->GetPEAssembly()->GetLoadedLayout(); _ASSERTE(pLoadedLayout != nullptr); ReadyToRunInfo::MethodIterator mi(pModule->GetReadyToRunInfo()); diff --git a/src/coreclr/vm/perfmap.h b/src/coreclr/vm/perfmap.h index 14595813877d8..587a776e68276 100644 --- a/src/coreclr/vm/perfmap.h +++ b/src/coreclr/vm/perfmap.h @@ -57,17 +57,17 @@ class PerfMap void LogMethod(MethodDesc * pMethod, PCODE pCode, size_t codeSize, const char *optimizationTier); // Does the actual work to log an image - void LogImage(PEFile * pFile); + void LogImage(PEAssembly * pPEAssembly); // Get the image signature and store it as a string. - static void GetNativeImageSignature(PEFile * pFile, WCHAR * pwszSig, unsigned int nSigSize); + static void GetNativeImageSignature(PEAssembly * pPEAssembly, WCHAR * pwszSig, unsigned int nSigSize); public: // Initialize the map for the current process. static void Initialize(); // Log a native image load to the map. - static void LogImageLoad(PEFile * pFile); + static void LogImageLoad(PEAssembly * pPEAssembly); // Log a JIT compiled method to the map. static void LogJITCompiledMethod(MethodDesc * pMethod, PCODE pCode, size_t codeSize, PrepareCodeConfig *pConfig); diff --git a/src/coreclr/vm/proftoeeinterfaceimpl.cpp b/src/coreclr/vm/proftoeeinterfaceimpl.cpp index 4f08f247da765..5c47a34f70487 100644 --- a/src/coreclr/vm/proftoeeinterfaceimpl.cpp +++ b/src/coreclr/vm/proftoeeinterfaceimpl.cpp @@ -2131,7 +2131,7 @@ HRESULT ProfToEEInterfaceImpl::GetTokenAndMetaDataFromFunction( // Yay! EE_THREAD_NOT_REQUIRED; - // PEFile::GetRWImporter and GetReadablePublicMetaDataInterface take locks + // PEAssembly::GetRWImporter and GetReadablePublicMetaDataInterface take locks CAN_TAKE_LOCK; } @@ -3977,11 +3977,11 @@ DWORD ProfToEEInterfaceImpl::GetModuleFlags(Module * pModule) } CONTRACTL_END; - PEFile * pPEFile = pModule->GetFile(); - if (pPEFile == NULL) + PEAssembly * pPEAssembly = pModule->GetPEAssembly(); + if (pPEAssembly == NULL) { // Hopefully this should never happen; but just in case, don't try to determine the - // flags without a PEFile. + // flags without a PEAssembly. return 0; } @@ -3996,15 +3996,15 @@ DWORD ProfToEEInterfaceImpl::GetModuleFlags(Module * pModule) dwRet |= (COR_PRF_MODULE_DISK | COR_PRF_MODULE_NGEN); } #endif - // Not NGEN or ReadyToRun. - if (pPEFile->HasOpenedILimage()) + // Not Dynamic. + if (pPEAssembly->HasPEImage()) { - PEImage * pILImage = pPEFile->GetOpenedILimage(); + PEImage * pILImage = pPEAssembly->GetPEImage(); if (pILImage->IsFile()) { dwRet |= COR_PRF_MODULE_DISK; } - if (pPEFile->GetLoadedIL()->IsFlat()) + if (pPEAssembly->GetLoadedLayout()->IsFlat()) { dwRet |= COR_PRF_MODULE_FLAT_LAYOUT; } @@ -4020,11 +4020,6 @@ DWORD ProfToEEInterfaceImpl::GetModuleFlags(Module * pModule) dwRet |= COR_PRF_MODULE_COLLECTIBLE; } - if (pModule->IsResource()) - { - dwRet |= COR_PRF_MODULE_RESOURCE; - } - return dwRet; } @@ -4086,7 +4081,7 @@ HRESULT ProfToEEInterfaceImpl::GetModuleInfo2(ModuleID moduleId, EX_TRY { - PEFile * pFile = pModule->GetFile(); + PEAssembly * pFile = pModule->GetPEAssembly(); // Pick some safe defaults to begin with. if (ppBaseLoadAddress != NULL) @@ -4209,7 +4204,7 @@ HRESULT ProfToEEInterfaceImpl::GetModuleMetaData(ModuleID moduleId, // but we might be able to lift that restriction and make this be // EE_THREAD_NOT_REQUIRED. - // PEFile::GetRWImporter & PEFile::GetEmitter & + // PEAssembly::GetRWImporter & PEAssembly::GetEmitter & // GetReadablePublicMetaDataInterface take locks CAN_TAKE_LOCK; @@ -4244,14 +4239,6 @@ HRESULT ProfToEEInterfaceImpl::GetModuleMetaData(ModuleID moduleId, return CORPROF_E_DATAINCOMPLETE; } - // Make sure we can get the importer first - if (pModule->IsResource()) - { - if (ppOut) - *ppOut = NULL; - return S_FALSE; - } - // Decide which type of open mode we are in to see which you require. if ((dwOpenFlags & ofWrite) == 0) { @@ -4299,7 +4286,7 @@ HRESULT ProfToEEInterfaceImpl::GetILFunctionBody(ModuleID moduleId, // Yay! MODE_ANY; - // PEFile::CheckLoaded & Module::GetDynamicIL both take a lock + // Module::GetDynamicIL both take a lock CAN_TAKE_LOCK; } @@ -4337,9 +4324,9 @@ HRESULT ProfToEEInterfaceImpl::GetILFunctionBody(ModuleID moduleId, IMDInternalImport *pImport = pModule->GetMDImport(); _ASSERTE(pImport); - PEFile *pFile = pModule->GetFile(); + PEAssembly *pPEAssembly = pModule->GetPEAssembly(); - if (!pFile->CheckLoaded()) + if (!pPEAssembly->HasLoadedPEImage()) return (CORPROF_E_DATAINCOMPLETE); LPCBYTE pbMethod = NULL; @@ -4354,7 +4341,7 @@ HRESULT ProfToEEInterfaceImpl::GetILFunctionBody(ModuleID moduleId, IfFailRet(pImport->GetMethodImplProps(methodId, &RVA, &dwImplFlags)); // Check to see if the method has associated IL - if ((RVA == 0 && !pFile->IsDynamic()) || !(IsMiIL(dwImplFlags) || IsMiOPTIL(dwImplFlags) || IsMiInternalCall(dwImplFlags))) + if ((RVA == 0 && !pPEAssembly->IsDynamic()) || !(IsMiIL(dwImplFlags) || IsMiOPTIL(dwImplFlags) || IsMiInternalCall(dwImplFlags))) { return (CORPROF_E_FUNCTION_NOT_IL); } @@ -4449,7 +4436,7 @@ HRESULT ProfToEEInterfaceImpl::GetILFunctionBodyAllocator(ModuleID modul Module * pModule = (Module *) moduleId; if (pModule->IsBeingUnloaded() || - !pModule->GetFile()->CheckLoaded()) + !pModule->GetPEAssembly()->HasLoadedPEImage()) { return (CORPROF_E_DATAINCOMPLETE); } @@ -4472,7 +4459,7 @@ HRESULT ProfToEEInterfaceImpl::SetILFunctionBody(ModuleID moduleId, { CONTRACTL { - // PEFile::GetEmitter, Module::SetDynamicIL all throw + // PEAssembly::GetEmitter, Module::SetDynamicIL all throw THROWS; // Locks are taken (see CAN_TAKE_LOCK below), which may cause mode switch to @@ -4482,7 +4469,7 @@ HRESULT ProfToEEInterfaceImpl::SetILFunctionBody(ModuleID moduleId, // Yay! MODE_ANY; - // Module::SetDynamicIL & PEFile::CheckLoaded & PEFile::GetEmitter take locks + // Module::SetDynamicIL & PEAssembly::GetEmitter take locks CAN_TAKE_LOCK; } diff --git a/src/coreclr/vm/readytoruninfo.cpp b/src/coreclr/vm/readytoruninfo.cpp index 4a6052b1ef162..bf77758eeccdf 100644 --- a/src/coreclr/vm/readytoruninfo.cpp +++ b/src/coreclr/vm/readytoruninfo.cpp @@ -392,7 +392,7 @@ BOOL ReadyToRunInfo::IsReadyToRunEnabled() // Any other value: Handle of the log file. static FILE * volatile s_r2rLogFile = (FILE *)(-1); -static void LogR2r(const char *msg, PEFile *pFile) +static void LogR2r(const char *msg, PEAssembly *pPEAssembly) { STANDARD_VM_CONTRACT; @@ -430,7 +430,7 @@ static void LogR2r(const char *msg, PEFile *pFile) if (r2rLogFile == NULL) return; - fprintf(r2rLogFile, "%s: \"%S\".\n", msg, pFile->GetPath().GetUnicode()); + fprintf(r2rLogFile, "%s: \"%S\".\n", msg, pPEAssembly->GetPath().GetUnicode()); fflush(r2rLogFile); } @@ -503,7 +503,7 @@ static NativeImage *AcquireCompositeImage(Module * pModule, PEImageLayout * pLay if (ownerCompositeExecutableName != NULL) { - AssemblyBinder *binder = pModule->GetFile()->GetAssemblyBinder(); + AssemblyBinder *binder = pModule->GetPEAssembly()->GetAssemblyBinder(); return binder->LoadNativeImage(pModule, ownerCompositeExecutableName); } @@ -514,7 +514,7 @@ PTR_ReadyToRunInfo ReadyToRunInfo::Initialize(Module * pModule, AllocMemTracker { STANDARD_VM_CONTRACT; - PEFile * pFile = pModule->GetFile(); + PEAssembly * pFile = pModule->GetPEAssembly(); if (!IsReadyToRunEnabled()) { @@ -529,13 +529,13 @@ PTR_ReadyToRunInfo ReadyToRunInfo::Initialize(Module * pModule, AllocMemTracker return NULL; } - if (!pFile->HasLoadedIL()) + if (!pFile->HasLoadedPEImage()) { - DoLog("Ready to Run disabled - no loaded IL image"); + DoLog("Ready to Run disabled - no loaded PE image"); return NULL; } - PEImageLayout * pLayout = pFile->GetLoadedIL(); + PEImageLayout * pLayout = pFile->GetLoadedLayout(); if (!pLayout->HasReadyToRunHeader()) { DoLog("Ready to Run header not found"); diff --git a/src/coreclr/vm/runtimehandles.cpp b/src/coreclr/vm/runtimehandles.cpp index 620ead8289e4f..5c436a73c9000 100644 --- a/src/coreclr/vm/runtimehandles.cpp +++ b/src/coreclr/vm/runtimehandles.cpp @@ -2728,7 +2728,7 @@ void QCALLTYPE ModuleHandle::GetPEKind(QCall::ModuleHandle pModule, DWORD* pdwPE QCALL_CONTRACT; BEGIN_QCALL; - pModule->GetFile()->GetPEKindAndMachine(pdwPEKind, pdwMachine); + pModule->GetPEAssembly()->GetPEKindAndMachine(pdwPEKind, pdwMachine); END_QCALL; } @@ -2742,10 +2742,6 @@ FCIMPL1(INT32, ModuleHandle::GetMDStreamVersion, ReflectModuleBaseObject * pModu FCThrowRes(kArgumentNullException, W("Arg_InvalidHandle")); Module *pModule = refModule->GetModule(); - - if (pModule->IsResource()) - return 0; - return pModule->GetMDImport()->GetMetadataStreamVersion(); } FCIMPLEND @@ -2787,10 +2783,6 @@ FCIMPL1(INT32, ModuleHandle::GetToken, ReflectModuleBaseObject * pModuleUNSAFE) FCThrowRes(kArgumentNullException, W("Arg_InvalidHandle")); Module *pModule = refModule->GetModule(); - - if (pModule->IsResource()) - return mdModuleNil; - return pModule->GetMDImport()->GetModuleFromScope(); } FCIMPLEND @@ -2805,10 +2797,6 @@ FCIMPL1(IMDInternalImport*, ModuleHandle::GetMetadataImport, ReflectModuleBaseOb FCThrowRes(kArgumentNullException, W("Arg_InvalidHandle")); Module *pModule = refModule->GetModule(); - - if (pModule->IsResource()) - return NULL; - return pModule->GetMDImport(); } FCIMPLEND diff --git a/src/coreclr/vm/siginfo.cpp b/src/coreclr/vm/siginfo.cpp index a3aea61ba7b2b..4c438360513cb 100644 --- a/src/coreclr/vm/siginfo.cpp +++ b/src/coreclr/vm/siginfo.cpp @@ -3547,7 +3547,7 @@ BOOL CompareTypeTokens(mdToken tk1, mdToken tk2, Module *pModule1, Module *pModu #ifdef DACCESS_COMPILE ThrowHR(hr); #else - EEFileLoadException::Throw(pModule2->GetFile(), hr); + EEFileLoadException::Throw(pModule2->GetPEAssembly(), hr); #endif //!DACCESS_COMPILE } // CompareTypeTokens diff --git a/src/coreclr/vm/typeparse.cpp b/src/coreclr/vm/typeparse.cpp index 84ce7124c473c..258b50d86916b 100644 --- a/src/coreclr/vm/typeparse.cpp +++ b/src/coreclr/vm/typeparse.cpp @@ -1415,7 +1415,7 @@ TypeName::GetTypeHaveAssemblyHelper( if (pManifestModule->LookupFile(mdFile)) continue; - pManifestModule->LoadModule(GetAppDomain(), mdFile, FALSE); + pManifestModule->LoadModule(GetAppDomain(), mdFile); th = GetTypeHaveAssemblyHelper(pAssembly, bThrowIfNotFound, bIgnoreCase, NULL, FALSE); @@ -1479,7 +1479,7 @@ DomainAssembly * LoadDomainAssembly( { // If the requesting assembly has Fallback LoadContext binder available, // then set it up in the AssemblySpec. - PEFile *pRequestingAssemblyManifestFile = pRequestingAssembly->GetManifestFile(); + PEAssembly *pRequestingAssemblyManifestFile = pRequestingAssembly->GetManifestFile(); spec.SetFallbackBinderForRequestingAssembly(pRequestingAssemblyManifestFile->GetFallbackBinder()); } From 937d4510f39677bd0f3769dcc5e4d0b83a9656ca Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Mon, 11 Oct 2021 21:16:55 +0000 Subject: [PATCH 037/106] =?UTF-8?q?[wasm][aot]=20Disable=20System.Runtime.?= =?UTF-8?q?Serialization.Xml.Tests,=20till=20they=E2=80=A6=20(#60258)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/libraries/tests.proj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index 2e1ffe3b100e9..67c03272de7ea 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -220,6 +220,9 @@ + + + From 825b26409226be4a29c7dcbe5aab97b979e3dac0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Cant=C3=BA?= Date: Mon, 11 Oct 2021 14:18:22 -0700 Subject: [PATCH 038/106] Fallback to read/write if pread/pwrite fails (#59846) * Fallback to read/write if pread/pwrite fails * * Cache whether a handle supports random access * Keep avoiding pread/pwrite for unseekable files * Fallback to read/write only for known error ENXIO * Test more device paths and add tests for WriteAll* * Fix CI: skip test when device can't be opened * * Do not use SkipTestException * Exclude EBADF from assertion * use NullableBool in _supportsRandomAccess * Add tests for mkfifo and socketpair * Rename test file * Address feedback in src * Address suggestions * Address suggestions * Update src/libraries/System.IO.FileSystem/tests/FileStream/DevicesPipesAndSockets.cs Use Lazy for thread-safety Co-authored-by: Adam Sitnik * Use IEnumerable in Lazy and fix usages Co-authored-by: David Cantu Co-authored-by: Adam Sitnik --- .../FileStream/DevicesPipesAndSockets.cs | 219 ++++++++++++++++++ .../tests/System.IO.FileSystem.Tests.csproj | 1 + .../Win32/SafeHandles/SafeFileHandle.Unix.cs | 20 ++ .../src/System/IO/RandomAccess.Unix.cs | 53 ++++- 4 files changed, 287 insertions(+), 6 deletions(-) create mode 100644 src/libraries/System.IO.FileSystem/tests/FileStream/DevicesPipesAndSockets.cs diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/DevicesPipesAndSockets.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/DevicesPipesAndSockets.cs new file mode 100644 index 0000000000000..ede852c7305b3 --- /dev/null +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/DevicesPipesAndSockets.cs @@ -0,0 +1,219 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.DotNet.XUnitExtensions; +using Microsoft.Win32.SafeHandles; +using System.Collections.Generic; +using System.IO.Pipes; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace System.IO.Tests +{ + [PlatformSpecific(TestPlatforms.AnyUnix)] + public class DevicesPipesAndSockets : FileSystemTest + { + [Theory] + [MemberData(nameof(DevicePath_FileOptions_TestData))] + public void CharacterDevice_FileStream_Write(string devicePath, FileOptions fileOptions) + { + FileStreamOptions options = new() { Options = fileOptions, Access = FileAccess.Write, Share = FileShare.Write }; + using FileStream fs = new(devicePath, options); + fs.Write(Encoding.UTF8.GetBytes("foo")); + } + + [Theory] + [MemberData(nameof(DevicePath_FileOptions_TestData))] + public async Task CharacterDevice_FileStream_WriteAsync(string devicePath, FileOptions fileOptions) + { + FileStreamOptions options = new() { Options = fileOptions, Access = FileAccess.Write, Share = FileShare.Write }; + using FileStream fs = new(devicePath, options); + await fs.WriteAsync(Encoding.UTF8.GetBytes("foo")); + } + + [Theory] + [MemberData(nameof(DevicePath_TestData))] + public void CharacterDevice_WriteAllBytes(string devicePath) + { + File.WriteAllBytes(devicePath, Encoding.UTF8.GetBytes("foo")); + } + + [Theory] + [MemberData(nameof(DevicePath_TestData))] + public async Task CharacterDevice_WriteAllBytesAsync(string devicePath) + { + await File.WriteAllBytesAsync(devicePath, Encoding.UTF8.GetBytes("foo")); + } + + [Theory] + [MemberData(nameof(DevicePath_TestData))] + public void CharacterDevice_WriteAllText(string devicePath) + { + File.WriteAllText(devicePath, "foo"); + } + + [Theory] + [MemberData(nameof(DevicePath_TestData))] + public async Task CharacterDevice_WriteAllTextAsync(string devicePath) + { + await File.WriteAllTextAsync(devicePath, "foo"); + } + + [Fact] + [PlatformSpecific(TestPlatforms.AnyUnix & ~TestPlatforms.Browser)] + public async Task NamedPipe_ReadWrite() + { + string fifoPath = GetTestFilePath(); + Assert.Equal(0, mkfifo(fifoPath, 438 /* 666 in octal */ )); + + await Task.WhenAll( + Task.Run(() => + { + using var fs = new FileStream(fifoPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + ReadByte(fs, 42); + }), + Task.Run(() => + { + using var fs = new FileStream(fifoPath, FileMode.Open, FileAccess.Write, FileShare.Read); + WriteByte(fs, 42); + })); + } + + [Fact] + [PlatformSpecific(TestPlatforms.AnyUnix & ~TestPlatforms.Browser)] + public async Task NamedPipe_ReadWrite_Async() + { + string fifoPath = GetTestFilePath(); + Assert.Equal(0, mkfifo(fifoPath, 438 /* 666 in octal */ )); + + await Task.WhenAll( + Task.Run(async () => { + using var fs = new FileStream(fifoPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + await ReadByteAsync(fs, 42); + }), + Task.Run(async () => + { + using var fs = new FileStream(fifoPath, FileMode.Open, FileAccess.Write, FileShare.Read); + await WriteByteAsync(fs, 42); + })); + } + + private const int AF_UNIX = 1; + private const int SOCK_STREAM = 1; + + [Fact] + [PlatformSpecific(TestPlatforms.AnyUnix & ~TestPlatforms.Browser)] + public unsafe void SocketPair_ReadWrite() + { + int* ptr = stackalloc int[2]; + Assert.Equal(0, socketpair(AF_UNIX, SOCK_STREAM, 0, ptr)); + + using var readFileStream = new FileStream(new SafeFileHandle((IntPtr)ptr[0], ownsHandle: true), FileAccess.Read); + using var writeFileStream = new FileStream(new SafeFileHandle((IntPtr)ptr[1], ownsHandle: true), FileAccess.Write); + + Task.WhenAll( + Task.Run(() => ReadByte(readFileStream, 42)), + Task.Run(() => WriteByte(writeFileStream, 42))).GetAwaiter().GetResult(); + } + + [Fact] + [PlatformSpecific(TestPlatforms.AnyUnix & ~TestPlatforms.Browser)] + public void SocketPair_ReadWrite_Async() + { + unsafe + { + int* ptr = stackalloc int[2]; + Assert.Equal(0, socketpair(AF_UNIX, SOCK_STREAM, 0, ptr)); + + using var readFileStream = new FileStream(new SafeFileHandle((IntPtr)ptr[0], ownsHandle: true), FileAccess.Read); + using var writeFileStream = new FileStream(new SafeFileHandle((IntPtr)ptr[1], ownsHandle: true), FileAccess.Write); + + Task.WhenAll( + ReadByteAsync(readFileStream, 42), + WriteByteAsync(writeFileStream, 42)).GetAwaiter().GetResult(); + } + } + + private static void ReadByte(FileStream fs, byte expected) + { + var buffer = new byte[1]; + Assert.Equal(1, fs.Read(buffer)); + Assert.Equal(expected, buffer[0]); + } + + private static void WriteByte(FileStream fs, byte value) + { + fs.Write(new byte[] { value }); + fs.Flush(); + } + + private static async Task ReadByteAsync(FileStream fs, byte expected) + { + var buffer = new byte[1]; + Assert.Equal(1, await fs.ReadAsync(buffer)); + Assert.Equal(expected, buffer[0]); + } + + private static async Task WriteByteAsync(FileStream fs, byte value) + { + await fs.WriteAsync(new byte[] { value }); + await fs.FlushAsync(); + } + + private static Lazy> AvailableDevicePaths = new Lazy>(() => + { + List paths = new(); + FileStreamOptions options = new() { Access = FileAccess.Write, Share = FileShare.Write }; + + foreach (string devicePath in new[] { "/dev/tty", "/dev/console", "/dev/null", "/dev/zero" }) + { + if (!File.Exists(devicePath)) + { + continue; + } + + try + { + File.Open(devicePath, options).Dispose(); + } + catch (Exception ex) + { + if (ex is IOException || ex is UnauthorizedAccessException) + { + continue; + } + + throw; + } + + paths.Add(devicePath); + } + + return paths; + }); + + public static IEnumerable DevicePath_FileOptions_TestData() + { + foreach (string devicePath in AvailableDevicePaths.Value) + { + foreach (FileOptions options in new[] { FileOptions.None, FileOptions.Asynchronous }) + { + yield return new object[] { devicePath, options}; + } + } + } + + public static IEnumerable DevicePath_TestData() + { + foreach (string devicePath in AvailableDevicePaths.Value) + { + yield return new object[] { devicePath }; + } + } + + [DllImport("libc")] + private static unsafe extern int socketpair(int domain, int type, int protocol, int* ptr); + } +} 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 1d523e70a1c8d..5533ba6a1a1ba 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 @@ -118,6 +118,7 @@ + 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 9b38d148b2693..13847032fbec7 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 @@ -16,6 +16,7 @@ public sealed partial class SafeFileHandle : SafeHandleZeroOrMinusOneIsInvalid // not using bool? as it's not thread safe private volatile NullableBool _canSeek = NullableBool.Undefined; + private volatile NullableBool _supportsRandomAccess = NullableBool.Undefined; private bool _deleteOnClose; private bool _isLocked; @@ -33,6 +34,25 @@ private SafeFileHandle(bool ownsHandle) internal bool CanSeek => !IsClosed && GetCanSeek(); + internal bool SupportsRandomAccess + { + get + { + NullableBool supportsRandomAccess = _supportsRandomAccess; + if (supportsRandomAccess == NullableBool.Undefined) + { + _supportsRandomAccess = supportsRandomAccess = GetCanSeek() ? NullableBool.True : NullableBool.False; + } + + return supportsRandomAccess == NullableBool.True; + } + set + { + Debug.Assert(value == false); // We should only use the setter to disable random access. + _supportsRandomAccess = value ? NullableBool.True : NullableBool.False; + } + } + internal ThreadPoolBoundHandle? ThreadPoolBinding => null; internal void EnsureThreadPoolBindingInitialized() { /* nop */ } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs index 992dcc54f5be5..d922e89c13de5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs @@ -33,9 +33,30 @@ internal static unsafe int ReadAtOffset(SafeFileHandle handle, Span buffer // The Windows implementation uses ReadFile, which ignores the offset if the handle // isn't seekable. We do the same manually with PRead vs Read, in order to enable // the function to be used by FileStream for all the same situations. - int result = handle.CanSeek ? - Interop.Sys.PRead(handle, bufPtr, buffer.Length, fileOffset) : - Interop.Sys.Read(handle, bufPtr, buffer.Length); + int result; + if (handle.SupportsRandomAccess) + { + // Try pread for seekable files. + result = Interop.Sys.PRead(handle, bufPtr, buffer.Length, fileOffset); + if (result == -1) + { + // We need to fallback to the non-offset version for certain file types + // e.g: character devices (such as /dev/tty), pipes, and sockets. + Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo(); + + if (errorInfo.Error == Interop.Error.ENXIO || + errorInfo.Error == Interop.Error.ESPIPE) + { + handle.SupportsRandomAccess = false; + result = Interop.Sys.Read(handle, bufPtr, buffer.Length); + } + } + } + else + { + result = Interop.Sys.Read(handle, bufPtr, buffer.Length); + } + FileStreamHelpers.CheckFileCall(result, handle.Path); return result; } @@ -90,9 +111,29 @@ internal static unsafe void WriteAtOffset(SafeFileHandle handle, ReadOnlySpan Date: Mon, 11 Oct 2021 15:12:58 -0700 Subject: [PATCH 039/106] Build using `build.cmd/sh`, not `bootstrap.cmd/sh` (#60264) --- src/coreclr/scripts/superpmi_setup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/scripts/superpmi_setup.py b/src/coreclr/scripts/superpmi_setup.py index 9ecec3bd9dd23..a8f2e136ee479 100644 --- a/src/coreclr/scripts/superpmi_setup.py +++ b/src/coreclr/scripts/superpmi_setup.py @@ -590,10 +590,10 @@ def make_readable(folder_name): dotnet_script_path = path.join(source_directory, dotnet_script_name) run_command([dotnet_script_path, "--info"], jitutils_directory) - # Set dotnet path to run bootstrap + # Set dotnet path to run build os.environ["PATH"] = path.join(source_directory, ".dotnet") + os.pathsep + os.environ["PATH"] - bootstrap_file = "bootstrap.cmd" if is_windows else "bootstrap.sh" - run_command([path.join(jitutils_directory, bootstrap_file)], jitutils_directory) + build_file = "build.cmd" if is_windows else "build.sh" + run_command([path.join(jitutils_directory, build_file), "-p"], jitutils_directory) copy_files(path.join(jitutils_directory, "bin"), superpmi_dst_directory, [path.join(jitutils_directory, "bin", "pmi.dll")]) except PermissionError as pe_error: From 4b8445190e67579f1f0c9e16df496dbec720f0b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Tue, 12 Oct 2021 00:56:23 +0200 Subject: [PATCH 040/106] Fix runtime-staging trigger and one bug (#60257) We were triggering the runtime-staging jobs for all branches rather than just PRs and the scheduled runs on main. Fix it to only run for pushes on release branches, like runtime.yml. I also noticed a small bug where we had `interpreter: true` in the "iOS/tvOS devices - Full AOT + AggressiveTrimming" job, that was probably a copy/paste mistake. --- eng/pipelines/runtime-staging.yml | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/eng/pipelines/runtime-staging.yml b/eng/pipelines/runtime-staging.yml index 92cca13420841..49fd3cdecb98d 100644 --- a/eng/pipelines/runtime-staging.yml +++ b/eng/pipelines/runtime-staging.yml @@ -1,3 +1,27 @@ +# Setting batch to true, triggers one build at a time. +# if there is a push while a build in progress, it will wait, +# until the running build finishes, and produce a build with all the changes +# that happened during the last build. +trigger: + batch: true + branches: + include: + - release/*.* + paths: + include: + - '*' + exclude: + - eng/Version.Details.xml + - .github/* + - docs/* + - CODE-OF-CONDUCT.md + - CONTRIBUTING.md + - LICENSE.TXT + - PATENTS.TXT + - README.md + - SECURITY.md + - THIRD-PARTY-NOTICES.TXT + schedules: - cron: "0 7,19 * * *" # run at 7:00 and 19:00 (UTC) which is 23:00 and 11:00 (PST). displayName: Runtime-staging default schedule @@ -160,7 +184,6 @@ jobs: extraStepsTemplate: /eng/pipelines/libraries/helix.yml extraStepsParameters: creator: dotnet-bot - interpreter: true testRunNamePrefixSuffix: Mono_$(_BuildConfig) condition: >- or( From debe671db16ec9ffb83e10b3f38d4a6993d9b7b5 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Mon, 11 Oct 2021 19:43:48 -0700 Subject: [PATCH 041/106] Take write lock in PEImage::LoadFlat/LoadImage (#60277) --- src/coreclr/vm/peimage.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/coreclr/vm/peimage.cpp b/src/coreclr/vm/peimage.cpp index 584797e16279b..4d8af1517b27a 100644 --- a/src/coreclr/vm/peimage.cpp +++ b/src/coreclr/vm/peimage.cpp @@ -976,8 +976,7 @@ PTR_PEImage PEImage::LoadFlat(const void *flat, COUNT_T size) PTR_PEImageLayout pLayout = PEImageLayout::CreateFlat(flat,size,pImage); _ASSERTE(!pLayout->IsMapped()); - //Not taking a lock here since we have just created pImage - //SimpleWriteLockHolder lock(pImage->m_pLayoutLock); + SimpleWriteLockHolder lock(pImage->m_pLayoutLock); pImage->SetLayout(IMAGE_FLAT,pLayout); RETURN dac_cast(pImage.Extract()); @@ -1001,8 +1000,7 @@ PTR_PEImage PEImage::LoadImage(HMODULE hMod) if (pImage->HasLoadedLayout()) RETURN dac_cast(pImage.Extract()); - //Not taking a lock here since we have just created pImage - // SimpleWriteLockHolder lock(pImage->m_pLayoutLock); + SimpleWriteLockHolder lock(pImage->m_pLayoutLock); if(pImage->m_pLayouts[IMAGE_LOADED]==NULL) pImage->SetLayout(IMAGE_LOADED,PEImageLayout::CreateFromHMODULE(hMod,pImage,WszGetModuleHandle(NULL)!=hMod)); @@ -1191,7 +1189,7 @@ HRESULT PEImage::TryOpenFile() return S_OK; { ErrorModeHolder mode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS); - m_hFile=WszCreateFile((LPCWSTR)GetPathToLoad(), + m_hFile=WszCreateFile((LPCWSTR)GetPathToLoad(), GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_DELETE, NULL, From df42364f8f1f33d2c16e385dc1652c490b3fbb8d Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Mon, 11 Oct 2021 23:15:51 -0700 Subject: [PATCH 042/106] Add C# multi-dimension array versions of the Benchstones tests (#60192) * Add C# multi-dimension array versions of the Benchstones tests The Benchstones tests using multi-dimension arrays currently are implemented using C# "jagged" arrays. Add versions of the tests that use true C# multi-dimensional arrays. The code is exactly the same, except for the array creation and indexing. This will allow us to compare code generated for each, as well as to verify functionality. I intend to add versions of these to the microbenchmarks in the https://github.com/dotnet/performance repo as well. * Fix MDPuzzle build --- .../BenchI/LogicArray/LogicArray.cs | 2 - .../Benchstones/MDBenchF/MDInProd/MDInProd.cs | 125 ++++ .../MDBenchF/MDInProd/MDInProd.csproj | 15 + .../Benchstones/MDBenchF/MDInvMt/MDInvMt.cs | 128 ++++ .../MDBenchF/MDInvMt/MDInvMt.csproj | 15 + .../Benchstones/MDBenchF/MDLLoops/MDLLoops.cs | 626 ++++++++++++++++++ .../MDBenchF/MDLLoops/MDLLoops.csproj | 15 + .../MDBenchF/MDLLoops/THIRD-PARTY-NOTICES | 17 + .../Benchstones/MDBenchF/MDRomber/MDRomber.cs | 160 +++++ .../MDBenchF/MDRomber/MDRomber.csproj | 15 + .../Benchstones/MDBenchF/MDSqMtx/MDSqMtx.cs | 94 +++ .../MDBenchF/MDSqMtx/MDSqMtx.csproj | 15 + .../MDBenchI/MDAddArray2/MDAddArray2.cs | 122 ++++ .../MDBenchI/MDAddArray2/MDAddArray2.csproj | 15 + .../Benchstones/MDBenchI/MDArray2/MDArray2.cs | 89 +++ .../MDBenchI/MDArray2/MDArray2.csproj | 15 + .../MDBenchI/MDLogicArray/MDLogicArray.cs | 88 +++ .../MDBenchI/MDLogicArray/MDLogicArray.csproj | 15 + .../MDBenchI/MDMidpoint/MDMidpoint.cs | 97 +++ .../MDBenchI/MDMidpoint/MDMidpoint.csproj | 15 + .../MDBenchI/MDMulMatrix/MDMulMatrix.cs | 133 ++++ .../MDBenchI/MDMulMatrix/MDMulMatrix.csproj | 15 + .../MDBenchI/MDNDhrystone/MDNDhrystone.cs | 279 ++++++++ .../MDBenchI/MDNDhrystone/MDNDhrystone.csproj | 15 + .../Benchstones/MDBenchI/MDPuzzle/MDPuzzle.cs | 383 +++++++++++ .../MDBenchI/MDPuzzle/MDPuzzle.csproj | 15 + .../MDBenchI/MDXposMatrix/MDXposMatrix.cs | 83 +++ .../MDBenchI/MDXposMatrix/MDXposMatrix.csproj | 15 + 28 files changed, 2619 insertions(+), 2 deletions(-) create mode 100644 src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDInProd/MDInProd.cs create mode 100644 src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDInProd/MDInProd.csproj create mode 100644 src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDInvMt/MDInvMt.cs create mode 100644 src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDInvMt/MDInvMt.csproj create mode 100644 src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDLLoops/MDLLoops.cs create mode 100644 src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDLLoops/MDLLoops.csproj create mode 100644 src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDLLoops/THIRD-PARTY-NOTICES create mode 100644 src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDRomber/MDRomber.cs create mode 100644 src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDRomber/MDRomber.csproj create mode 100644 src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDSqMtx/MDSqMtx.cs create mode 100644 src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDSqMtx/MDSqMtx.csproj create mode 100644 src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDAddArray2/MDAddArray2.cs create mode 100644 src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDAddArray2/MDAddArray2.csproj create mode 100644 src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDArray2/MDArray2.cs create mode 100644 src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDArray2/MDArray2.csproj create mode 100644 src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDLogicArray/MDLogicArray.cs create mode 100644 src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDLogicArray/MDLogicArray.csproj create mode 100644 src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDMidpoint/MDMidpoint.cs create mode 100644 src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDMidpoint/MDMidpoint.csproj create mode 100644 src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDMulMatrix/MDMulMatrix.cs create mode 100644 src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDMulMatrix/MDMulMatrix.csproj create mode 100644 src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDNDhrystone/MDNDhrystone.cs create mode 100644 src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDNDhrystone/MDNDhrystone.csproj create mode 100644 src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDPuzzle/MDPuzzle.cs create mode 100644 src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDPuzzle/MDPuzzle.csproj create mode 100644 src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDXposMatrix/MDXposMatrix.cs create mode 100644 src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDXposMatrix/MDXposMatrix.csproj diff --git a/src/tests/JIT/Performance/CodeQuality/Benchstones/BenchI/LogicArray/LogicArray.cs b/src/tests/JIT/Performance/CodeQuality/Benchstones/BenchI/LogicArray/LogicArray.cs index c8b5864fb4ed0..00e4c5c3e33c9 100644 --- a/src/tests/JIT/Performance/CodeQuality/Benchstones/BenchI/LogicArray/LogicArray.cs +++ b/src/tests/JIT/Performance/CodeQuality/Benchstones/BenchI/LogicArray/LogicArray.cs @@ -20,8 +20,6 @@ public static class LogicArray public const int Iterations = 3000; #endif - const int ArraySize = 50; - struct Workarea { public int X; diff --git a/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDInProd/MDInProd.cs b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDInProd/MDInProd.cs new file mode 100644 index 0000000000000..fa3ce9643c6a3 --- /dev/null +++ b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDInProd/MDInProd.cs @@ -0,0 +1,125 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +using Microsoft.Xunit.Performance; +using System; +using System.Runtime.CompilerServices; +using Xunit; + +[assembly: OptimizeForBenchmarks] + +namespace Benchstone.MDBenchF +{ +public static class MDInProd +{ +#if DEBUG + public const int Iterations = 1; +#else + public const int Iterations = 70; +#endif + + private const int RowSize = 10 * Iterations; + + private static int s_seed; + + [MethodImpl(MethodImplOptions.NoInlining)] + private static bool Bench() + { + double[,] rma = new double[RowSize, RowSize]; + double[,] rmb = new double[RowSize, RowSize]; + double[,] rmr = new double[RowSize, RowSize]; + + double sum; + + Inner(rma, rmb, rmr); + + for (int i = 1; i < RowSize; i++) + { + for (int j = 1; j < RowSize; j++) + { + sum = 0; + for (int k = 1; k < RowSize; k++) + { + sum = sum + rma[i,k] * rmb[k,j]; + } + if (rmr[i,j] != sum) + { + return false; + } + } + } + + return true; + } + + private static void InitRand() + { + s_seed = 7774755; + } + + private static int Rand() + { + s_seed = (s_seed * 77 + 13218009) % 3687091; + return s_seed; + } + + private static void InitMatrix(double[,] m) + { + for (int i = 1; i < RowSize; i++) + { + for (int j = 1; j < RowSize; j++) + { + m[i,j] = (Rand() % 120 - 60) / 3; + } + } + } + + private static void InnerProduct(out double result, double[,] a, double[,] b, int row, int col) + { + result = 0.0; + for (int i = 1; i < RowSize; i++) + { + result = result + a[row,i] * b[i,col]; + } + } + + private static void Inner(double[,] rma, double[,] rmb, double[,] rmr) + { + InitRand(); + InitMatrix(rma); + InitMatrix(rmb); + for (int i = 1; i < RowSize; i++) + { + for (int j = 1; j < RowSize; j++) + { + InnerProduct(out rmr[i,j], rma, rmb, i, j); + } + } + } + + [Benchmark] + public static void Test() + { + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + Bench(); + } + } + } + + private static bool TestBase() + { + bool result = Bench(); + return result; + } + + public static int Main() + { + bool result = TestBase(); + return (result ? 100 : -1); + } +} +} diff --git a/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDInProd/MDInProd.csproj b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDInProd/MDInProd.csproj new file mode 100644 index 0000000000000..0a051a8d688d8 --- /dev/null +++ b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDInProd/MDInProd.csproj @@ -0,0 +1,15 @@ + + + Exe + + + pdbonly + true + + + + + + $(JitPackagesConfigFileDirectory)benchmark\obj\project.assets.json + + diff --git a/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDInvMt/MDInvMt.cs b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDInvMt/MDInvMt.cs new file mode 100644 index 0000000000000..32b25b9afe7f0 --- /dev/null +++ b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDInvMt/MDInvMt.cs @@ -0,0 +1,128 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// Solution of linear algebraic equations and matrix inversion. + +using Microsoft.Xunit.Performance; +using System; +using System.Runtime.CompilerServices; +using Xunit; + +[assembly: OptimizeForBenchmarks] + +namespace Benchstone.MDBenchF +{ +public static class MDInvMt +{ +#if DEBUG + public const int Iterations = 1; +#else + public const int Iterations = 80; +#endif + + private const int MatSize = Iterations; + + [MethodImpl(MethodImplOptions.NoInlining)] + private static bool Bench() + { + double[,] t = new double[MatSize + 1, (MatSize + 1) * 2]; + + double det, detinv, ber, p; + int n, i, j; + + n = MatSize; + for (i = 1; i <= n; i++) + { + for (j = 1; j <= n; j++) + { + if (i == j) + { + t[i,j] = 2.0001; + t[i,n + 1 + j] = 1.0; + } + else + { + t[i,j] = 1.0001; + t[i,n + 1 + j] = 0.0; + } + } + t[i,n + 1] = System.Math.Sqrt((float)i); + } + + Inner(t, out det, ref n); + + for (i = 1; i <= n; i++) + { + for (j = 1; j <= n; j++) + { + p = t[i,j]; + t[i,j] = t[i,n + 1 + j]; + t[i,n + 1 + j] = p; + } + } + + Inner(t, out detinv, ref n); + + ber = 0.0; + for (i = 1; i <= n; i++) + { + ber = ber + System.Math.Abs(System.Math.Sqrt((double)i) - t[i,n + 1]); + } + + return true; + } + + private static void Inner(double[,] t, out double det, ref int n) + { + double tik, tkk; + + det = 1.0; + for (int k = 1; k <= n; k++) + { + tkk = t[k,k]; + det = det * tkk; + + for (int j = 1; j <= (2 * n + 1); j++) + { + t[k,j] = t[k,j] / tkk; + } + + for (int i = 1; i <= n; i++) + { + if (i != k) + { + tik = t[i,k]; + for (int j = 1; j <= (2 * n + 1); j++) + { + t[i,j] = t[i,j] - t[k,j] * tik; + } + } + } + } + } + + [Benchmark] + public static void Test() + { + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + Bench(); + } + } + } + + private static bool TestBase() + { + bool result = Bench(); + return result; + } + + public static int Main() + { + bool result = TestBase(); + return (result ? 100 : -1); + } +} +} diff --git a/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDInvMt/MDInvMt.csproj b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDInvMt/MDInvMt.csproj new file mode 100644 index 0000000000000..0a051a8d688d8 --- /dev/null +++ b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDInvMt/MDInvMt.csproj @@ -0,0 +1,15 @@ + + + Exe + + + pdbonly + true + + + + + + $(JitPackagesConfigFileDirectory)benchmark\obj\project.assets.json + + diff --git a/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDLLoops/MDLLoops.cs b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDLLoops/MDLLoops.cs new file mode 100644 index 0000000000000..ef4c6a5ddec2f --- /dev/null +++ b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDLLoops/MDLLoops.cs @@ -0,0 +1,626 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// C# adaptation of C implementation of Livermore Loops Fortran benchmark. + +/* Livermore Loops coded in C Latest File Modification 20 Oct 92, + * by Tim Peters, Kendall Square Res. Corp. tim@ksr.com, ksr!tim@uunet.uu.net + * SUBROUTINE KERNEL( TK) replaces the Fortran routine in LFK Test program. + ************************************************************************ + * * + * KERNEL executes 24 samples of "C" computation * + * * + * TK(1) - total cpu time to execute only the 24 kernels.* + * TK(2) - total Flops executed by the 24 Kernels * + * * + ************************************************************************ + * * + * L. L. N. L. " C " K E R N E L S: M F L O P S * + * * + * These kernels measure " C " numerical computation * + * rates for a spectrum of cpu-limited computational * + * structures or benchmarks. Mathematical through-put * + * is measured in units of millions of floating-point * + * operations executed per second, called Megaflops/sec. * + * * + * Fonzi's Law: There is not now and there never will be a language * + * in which it is the least bit difficult to write * + * bad programs. * + * F.H.MCMAHON 1972 * + ************************************************************************ + *Originally from Greg Astfalk, AT&T, P.O.Box 900, Princeton, NJ. 08540* + * by way of Frank McMahon (LLNL). * + * * + * REFERENCE * + * * + * F.H.McMahon, The Livermore Fortran Kernels: * + * A Computer Test Of The Numerical Performance Range, * + * Lawrence Livermore National Laboratory, * + * Livermore, California, UCRL-53745, December 1986. * + * * + * from: National Technical Information Service * + * U.S. Department of Commerce * + * 5285 Port Royal Road * + * Springfield, VA. 22161 * + * * + * Changes made to correct many array subscripting problems, * + * make more readable (added #define's), include the original * + * FORTRAN versions of the runs as comments, and make more * + * portable by Kelly O'Hair (LLNL) and Chuck Rasbold (LLNL). * + * * + ************************************************************************ + */ + +using Microsoft.Xunit.Performance; +using System; +using System.Runtime.CompilerServices; +using Xunit; + +[assembly: OptimizeForBenchmarks] + +namespace Benchstone.MDBenchF +{ +public class MDLLoops +{ +#if DEBUG + public const int Iterations = 1; +#else + public const int Iterations = 4000; +#endif + + private const double MaxErr = 1.0e-6; + + private double[] _x = new double[1002]; + private double[] _y = new double[1002]; + private double[] _z = new double[1002]; + private double[] _u = new double[501]; + private double[,] _px; + private double[,] _cx; + private double[,,] _u1; + private double[,,] _u2; + private double[,,] _u3; + private double[,] _b; + private double[] _bnk1 = new double[6]; + private double[,] _c; + private double[] _bnk2 = new double[6]; + private double[,] _p; + private double[] _bnk3 = new double[6]; + private double[,] _h; + private double[] _bnk4 = new double[6]; + private double[] _bnk5 = new double[6]; + private double[] _ex = new double[68]; + private double[] _rh = new double[68]; + private double[] _dex = new double[68]; + private double[] _vx = new double[151]; + private double[] _xx = new double[151]; + private double[] _grd = new double[151]; + private int[] _e = new int[193]; + private int[] _f = new int[193]; + private int[] _nrops = { 0, 5, 10, 2, 2, 2, 2, 16, 36, 17, 9, 1, 1, 7, 11 }; + private int[] _loops = { 0, 400, 200, 1000, 510, 1000, 1000, 120, 40, 100, 100, 1000, 1000, 128, 150 }; + private double[] _checks = { + 0, 0.811986948148e+07, 0.356310000000e+03, 0.356310000000e+03, -0.402412007078e+05, + 0.136579037764e+06, 0.419716278716e+06, + 0.429449847526e+07, 0.314064400000e+06, + 0.182709000000e+07, -0.140415250000e+09, + 0.374895020500e+09, 0.000000000000e+00, + 0.171449024000e+06, -0.510829560800e+07 + }; + + public static volatile object VolatileObject; + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void Escape(object obj) + { + VolatileObject = obj; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private bool Bench() + { + _px = new double[16, 101]; + _cx = new double[16, 101]; + + _u1 = new double[6, 23, 3]; + _u2 = new double[6, 23, 3]; + _u3 = new double[6, 23, 3]; + + _b = new double[65, 9]; + _c = new double[65, 9]; + _h = new double[65, 9]; + + _p = new double[5, 513]; + + for (int i = 0; i < Iterations; i++) + { + Main1(i < Iterations - 1 ? 0 : 1); + } + + return true; + } + + private static int Clock() + { + return 0; + } + + private void Main1(int output) + { + int nt, lw, nl1, nl2; + int i, i1, i2, ip, ir, ix, j, j1, j2, k, kx, ky, l, m; + double[] ts = new double[21]; + double[] rt = new double[21]; + double[] rpm = new double[21]; + double[] cksum = new double[21]; + double r, t, a11, a12, a13, sig, a21, a22, a23, a31, a32, a33; + double b28, b27, b26, b25, b24, b23, b22, c0, flx, rx1; + double q, s, scale, uu, du1, du2, du3, ar, br, cr, xi, ri; + int[] mops = new int[20]; + + for (i = 1; i <= 20; i++) + { + cksum[i] = 0.0; + } + + r = 4.86; + t = 276.0; + a11 = 0.5; + a12 = 0.33; + a13 = 0.25; + sig = 0.8; + a21 = 0.20; + a22 = 0.167; + a23 = 0.141; + a31 = 0.125; + a32 = 0.111; + a33 = 0.10; + b28 = 0.1; + b27 = 0.2; + b26 = 0.3; + b25 = 0.4; + b24 = 0.5; + b23 = 0.6; + b22 = 0.7; + c0 = 0.8; + flx = 4.689; + rx1 = 64.0; + + /* + * end of initialization -- begin timing + */ + + /* loop 1 hydro excerpt */ + + Init(); + ts[1] = (double)Clock(); + q = 0.0; + for (k = 1; k <= 400; k++) + { + _x[k] = q + _y[k] * (r * _z[k + 10] + t * _z[k + 11]); + } + ts[1] = (double)Clock() - ts[1]; + for (k = 1; k <= 400; k++) + { + cksum[1] += (double)k * _x[k]; + } + + /* loop 2 mlr, inner product */ + + Init(); + ts[2] = (double)Clock(); + q = 0.0; + for (k = 1; k <= 996; k += 5) + { + q += _z[k] * _x[k] + _z[k + 1] * _x[k + 1] + _z[k + 2] * _x[k + 2] + _z[k + 3] * _x[k + 3] + _z[k + 4] * _x[k + 4]; + } + ts[2] = (double)Clock() - ts[2]; + cksum[2] = q; + + /* loop 3 inner prod */ + + Init(); + ts[3] = (double)Clock(); + q = 0.0; + for (k = 1; k <= 1000; k++) + { + q += _z[k] * _x[k]; + } + ts[3] = (double)Clock() - ts[3]; + cksum[3] = q; + + /* loop 4 banded linear equarions */ + + Init(); + ts[4] = (double)Clock(); + for (l = 7; l <= 107; l += 50) + { + lw = l; + for (j = 30; j <= 870; j += 5) + { + _x[l - 1] -= _x[lw++] * _y[j]; + } + _x[l - 1] = _y[5] * _x[l - 1]; + } + ts[4] = (double)Clock() - ts[4]; + for (l = 7; l <= 107; l += 50) + { + cksum[4] += (double)l * _x[l - 1]; + } + + /* loop 5 tri-diagonal elimination, below diagonal */ + + Init(); + ts[5] = (double)Clock(); + for (i = 2; i <= 998; i += 3) + { + _x[i] = _z[i] * (_y[i] - _x[i - 1]); + _x[i + 1] = _z[i + 1] * (_y[i + 1] - _x[i]); + _x[i + 2] = _z[i + 2] * (_y[i + 2] - _x[i + 1]); + } + ts[5] = (double)Clock() - ts[5]; + for (i = 2; i <= 1000; i++) + { + cksum[5] += (double)i * _x[i]; + } + + /* loop 6 tri-diagonal elimination, above diagonal */ + + Init(); + ts[6] = (double)Clock(); + for (j = 3; j <= 999; j += 3) + { + i = 1003 - j; + _x[i] = _x[i] - _z[i] * _x[i + 1]; + _x[i - 1] = _x[i - 1] - _z[i - 1] * _x[i]; + _x[i - 2] = _x[i - 2] - _z[i - 2] * _x[i - 1]; + } + ts[6] = (double)Clock() - ts[6]; + for (j = 1; j <= 999; j++) + { + l = 1001 - j; + cksum[6] += (double)j * _x[l]; + } + + /* loop 7 equation of state excerpt */ + + Init(); + ts[7] = (double)Clock(); + for (m = 1; m <= 120; m++) + { + _x[m] = _u[m] + r * (_z[m] + r * _y[m]) + t * (_u[m + 3] + r * (_u[m + 2] + r * _u[m + 1]) + t * (_u[m + 6] + r * (_u[m + 5] + r * _u[m + 4]))); + } + ts[7] = (double)Clock() - ts[7]; + for (m = 1; m <= 120; m++) + { + cksum[7] += (double)m * _x[m]; + } + + /* loop 8 p.d.e. integration */ + + Init(); + ts[8] = (double)Clock(); + nl1 = 1; + nl2 = 2; + for (kx = 2; kx <= 3; kx++) + { + for (ky = 2; ky <= 21; ky++) + { + du1 = _u1[kx,ky + 1,nl1] - _u1[kx,ky - 1,nl1]; + du2 = _u2[kx,ky + 1,nl1] - _u2[kx,ky - 1,nl1]; + du3 = _u3[kx,ky + 1,nl1] - _u3[kx,ky - 1,nl1]; + _u1[kx,ky,nl2] = _u1[kx,ky,nl1] + a11 * du1 + a12 * du2 + a13 * du3 + sig * (_u1[kx + 1,ky,nl1] + - 2.0 * _u1[kx,ky,nl1] + _u1[kx - 1,ky,nl1]); + _u2[kx,ky,nl2] = _u2[kx,ky,nl1] + a21 * du1 + a22 * du2 + a23 * du3 + sig * (_u2[kx + 1,ky,nl1] + - 2.0 * _u2[kx,ky,nl1] + _u2[kx - 1,ky,nl1]); + _u3[kx,ky,nl2] = _u3[kx,ky,nl1] + a31 * du1 + a32 * du2 + a33 * du3 + sig * (_u3[kx + 1,ky,nl1] + - 2.0 * _u3[kx,ky,nl1] + _u3[kx - 1,ky,nl1]); + } + } + ts[8] = (double)Clock() - ts[8]; + for (i = 1; i <= 2; i++) + { + for (kx = 2; kx <= 3; kx++) + { + for (ky = 2; ky <= 21; ky++) + { + cksum[8] += (double)kx * (double)ky * (double)i * (_u1[kx,ky,i] + _u2[kx,ky,i] + _u3[kx,ky,i]); + } + } + } + + /* loop 9 integrate predictors */ + + Init(); + ts[9] = (double)Clock(); + for (i = 1; i <= 100; i++) + { + _px[1,i] = b28 * _px[13,i] + b27 * _px[12,i] + b26 * _px[11,i] + b25 * _px[10,i] + b24 * _px[9,i] + + b23 * _px[8,i] + b22 * _px[7,i] + c0 * (_px[5,i] + _px[6,i]) + _px[3,i]; + } + ts[9] = (double)Clock() - ts[9]; + for (i = 1; i <= 100; i++) + { + cksum[9] += (double)i * _px[1,i]; + } + + /* loop 10 difference predictors */ + + Init(); + ts[10] = (double)Clock(); + for (i = 1; i <= 100; i++) + { + ar = _cx[5,i]; + br = ar - _px[5,i]; + _px[5,i] = ar; + cr = br - _px[6,i]; + _px[6,i] = br; + ar = cr - _px[7,i]; + _px[7,i] = cr; + br = ar - _px[8,i]; + _px[8,i] = ar; + cr = br - _px[9,i]; + _px[9,i] = br; + ar = cr - _px[10,i]; + _px[10,i] = cr; + br = ar - _px[11,i]; + _px[11,i] = ar; + cr = br - _px[12,i]; + _px[12,i] = br; + _px[14,i] = cr - _px[13,i]; + _px[13,i] = cr; + } + ts[10] = (double)Clock() - ts[10]; + for (i = 1; i <= 100; i++) + { + for (k = 5; k <= 14; k++) + { + cksum[10] += (double)k * (double)i * _px[k,i]; + } + } + + /* loop 11 first sum. */ + + Init(); + ts[11] = (double)Clock(); + _x[1] = _y[1]; + for (k = 2; k <= 1000; k++) + { + _x[k] = _x[k - 1] + _y[k]; + } + ts[11] = (double)Clock() - ts[11]; + for (k = 1; k <= 1000; k++) + { + cksum[11] += (double)k * _x[k]; + } + + /* loop 12 first diff. */ + + Init(); + ts[12] = (double)Clock(); + for (k = 1; k <= 999; k++) + { + _x[k] = _y[k + 1] - _y[k]; + } + ts[12] = (double)Clock() - ts[12]; + for (k = 1; k <= 999; k++) + { + cksum[12] += (double)k * _x[k]; + } + + /* loop 13 2-d particle pusher */ + + Init(); + ts[13] = (double)Clock(); + for (ip = 1; ip <= 128; ip++) + { + i1 = (int)_p[1,ip]; + j1 = (int)_p[2,ip]; + _p[3,ip] += _b[i1,j1]; + _p[4,ip] += _c[i1,j1]; + _p[1,ip] += _p[3,ip]; + _p[2,ip] += _p[4,ip]; + // Each element of m_p, m_b and m_c is initialized to 1.00025 in Init(). + // From the assignments above, + // i2 = m_p[1,ip] = m_p[1,ip] + m_p[3,ip] = m_p[1,ip] + m_p[3,ip] + m_b[i1,j1] = 1 + 1 + 1 = 3 + // j2 = m_p[2,ip] = m_p[2,ip] + m_p[4,ip] = m_p[2,ip] + m_p[4,ip] + m_c[i1,j1] = 1 + 1 + 1 = 3 + i2 = (int)_p[1,ip]; + j2 = (int)_p[2,ip]; + // Accessing m_y, m_z upto 35 + _p[1,ip] += _y[i2 + 32]; + _p[2,ip] += _z[j2 + 32]; + + i2 += _e[i2 + 32]; + j2 += _f[j2 + 32]; + _h[i2,j2] += 1.0; + } + ts[13] = (double)Clock() - ts[13]; + for (ip = 1; ip <= 128; ip++) + { + cksum[13] += (double)ip * (_p[3,ip] + _p[4,ip] + _p[1,ip] + _p[2,ip]); + } + for (k = 1; k <= 64; k++) + { + for (ix = 1; ix <= 8; ix++) + { + cksum[13] += (double)k * (double)ix * _h[k,ix]; + } + } + + /* loop 14 1-d particle pusher */ + + Init(); + ts[14] = (double)Clock(); + for (k = 1; k <= 150; k++) + { + // m_grd[150] = 13.636 + // Therefore ix <= 13 + ix = (int)_grd[k]; + xi = (double)ix; + _vx[k] += _ex[ix] + (_xx[k] - xi) * _dex[ix]; + _xx[k] += _vx[k] + flx; + ir = (int)_xx[k]; + ri = (double)ir; + rx1 = _xx[k] - ri; + ir = System.Math.Abs(ir % 64); + _xx[k] = ri + rx1; + // ir < 64 since ir = ir % 64 + // So m_rh is accessed upto 64 + _rh[ir] += 1.0 - rx1; + _rh[ir + 1] += rx1; + } + ts[14] = (double)Clock() - ts[14]; + for (k = 1; k <= 150; k++) + { + cksum[14] += (double)k * (_vx[k] + _xx[k]); + } + for (k = 1; k <= 67; k++) + { + cksum[14] += (double)k * _rh[k]; + } + + /* time the clock call */ + + ts[15] = (double)Clock(); + ts[15] = (double)Clock() - ts[15]; + + /* scale= set to convert time to micro-seconds */ + + scale = 1.0; + rt[15] = ts[15] * scale; + + nt = 14; + t = s = uu = 0.0; + for (k = 1; k <= nt; k++) + { + rt[k] = (ts[k] - ts[15]) * scale; + t += rt[k]; + mops[k] = _nrops[k] * _loops[k]; + s += (double)mops[k]; + rpm[k] = 0.0; + if (rt[k] != 0.0) + { + rpm[k] = (double)mops[k] / rt[k]; + } + uu += rpm[k]; + } + uu /= (double)nt; + s /= t; + + // Ensure that the array elements are live-out + Escape(ts); + Escape(rt); + Escape(rpm); + Escape(cksum); + Escape(mops); + } + + private void Init() + { + int j, k, l; + + for (k = 1; k <= 1000; k++) + { + _x[k] = 1.11; + _y[k] = 1.123; + _z[k] = 0.321; + } + + for (k = 1; k <= 500; k++) + { + _u[k] = 0.00025; + } + + for (k = 1; k <= 15; k++) + { + for (l = 1; l <= 100; l++) + { + _px[k,l] = l; + _cx[k,l] = l; + } + } + + for (j = 1; j < 6; j++) + { + for (k = 1; k < 23; k++) + { + for (l = 1; l < 3; l++) + { + _u1[j,k,l] = k; + _u2[j,k,l] = k + k; + _u3[j,k,l] = k + k + k; + } + } + } + + for (j = 1; j < 65; j++) + { + for (k = 1; k < 9; k++) + { + _b[j,k] = 1.00025; + _c[j,k] = 1.00025; + _h[j,k] = 1.00025; + } + } + + for (j = 1; j < 6; j++) + { + _bnk1[j] = j * 100; + _bnk2[j] = j * 110; + _bnk3[j] = j * 120; + _bnk4[j] = j * 130; + _bnk5[j] = j * 140; + } + + for (j = 1; j < 5; j++) + { + for (k = 1; k < 513; k++) + { + _p[j,k] = 1.00025; + } + } + + for (j = 1; j < 193; j++) + { + _e[j] = _f[j] = 1; + } + + for (j = 1; j < 68; j++) + { + _ex[j] = _rh[j] = _dex[j] = (double)j; + } + + for (j = 1; j < 151; j++) + { + _vx[j] = 0.001; + _xx[j] = 0.001; + _grd[j] = (double)(j / 8 + 3); + } + } + + [Benchmark] + public static void Test() + { + var lloops = new MDLLoops(); + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + lloops.Bench(); + } + } + } + + private bool TestBase() + { + bool result = Bench(); + return result; + } + + public static int Main() + { + var lloops = new MDLLoops(); + bool result = lloops.TestBase(); + return (result ? 100 : -1); + } +} +} diff --git a/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDLLoops/MDLLoops.csproj b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDLLoops/MDLLoops.csproj new file mode 100644 index 0000000000000..0a051a8d688d8 --- /dev/null +++ b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDLLoops/MDLLoops.csproj @@ -0,0 +1,15 @@ + + + Exe + + + pdbonly + true + + + + + + $(JitPackagesConfigFileDirectory)benchmark\obj\project.assets.json + + diff --git a/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDLLoops/THIRD-PARTY-NOTICES b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDLLoops/THIRD-PARTY-NOTICES new file mode 100644 index 0000000000000..7e0bac5f317d6 --- /dev/null +++ b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDLLoops/THIRD-PARTY-NOTICES @@ -0,0 +1,17 @@ +.NET Core uses third-party libraries or other resources that may be +distributed under licenses different than the .NET Core software. + +In the event that we accidentally failed to list a required notice, please +bring it to our attention. Post an issue or email us: + + dotnet@microsoft.com + +The attached notices are provided for information only. + +License notice for Livermore Loops coded in C +--------------------------------------------- + +http://www.netlib.org/benchmark/livermorec + +No specific license is given, so attributing and using in "good faith" +in the same way that it has been offered. We will delete upon request. diff --git a/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDRomber/MDRomber.cs b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDRomber/MDRomber.cs new file mode 100644 index 0000000000000..c5235c67d1fe4 --- /dev/null +++ b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDRomber/MDRomber.cs @@ -0,0 +1,160 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// Integration by romberg method adapted from Conte and de Boor + +using Microsoft.Xunit.Performance; +using System; +using System.Runtime.CompilerServices; +using Xunit; + +[assembly: OptimizeForBenchmarks] + +namespace Benchstone.MDBenchF +{ +public static class MDRomber +{ +#if DEBUG + public const int Iterations = 1; +#else + public const int Iterations = 640000; +#endif + + [MethodImpl(MethodImplOptions.NoInlining)] + private static bool Bench() + { + double[,] r = new double[11, 11]; + double[,] t = new double[11, 11]; + + int idbg, m, n, i, kmax, fourj, j, kmaxm2, l, k, mm1; + double sum, ratio, t1, h, a, b; + + for (l = 1; l <= Iterations; l++) + { + idbg = 0; + m = 2; + kmax = 6; + a = 0; + b = 1; + h = (b - a) / (m); + sum = (F(a) + F(b)) / 2; + + mm1 = m - 1; + if (mm1 < 0) + { + goto L40; + } + if (mm1 == 0) + { + goto L10; + } + for (i = 1; i <= mm1; i++) + { + t1 = a + i * h; + sum = sum + F(t1); + } + + L10: + t[1,1] = sum * h; + if (idbg != 0) + { + System.Console.WriteLine(" romberg t-table \n"); + System.Console.WriteLine("{0}\n", t[1,1]); + } + + for (k = 2; k <= kmax; k++) + { + h = h / 2; + n = m * 2; + sum = 0; + for (i = 1; i <= n / 2; i++) + { + r[k,1] = r[k - 1,1] * System.Math.Sqrt(b * mm1); + t1 = a + i * h; + sum = sum + F(t1); + } + + t[k,1] = t[k - 1,1] / 2 + sum * h; + fourj = 1; + for (j = 2; j <= k; j++) + { + fourj = fourj * 4; + t[k - 1,j - 1] = t[k,j - 1] - t[k - 1,j - 1]; + t[k,j] = t[k,j - 1] + t[k - 1,j - 1] / (fourj - 1); + } + + if (idbg != 0) + { + j = 1; + System.Console.WriteLine("{0} {1} {2}d\n", t[k,j], j, k); + } + } + + kmaxm2 = kmax - 2; + if (kmaxm2 <= 0) + { + goto L40; + } + + if (idbg != 0) + { + System.Console.WriteLine(" table of ratios \n"); + } + + for (k = 1; k <= kmaxm2; k++) + { + for (j = 1; j <= k; j++) + { + ratio = 0; + if (System.Math.Abs(t[k + 1,j]) > 0) + { + ratio = t[k,j] / t[k + 1,j]; + } + t[k,j] = ratio; + } + } + + if (idbg != 0) + { + j = 1; + System.Console.WriteLine("{0} {1} {2}\n", t[k,j], j, k); + } + + L40: + { + } + } + + return true; + } + + private static double F(double x) + { + return (System.Math.Exp((-(x)) * (x))); + } + + [Benchmark] + public static void Test() + { + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + Bench(); + } + } + } + + private static bool TestBase() + { + bool result = Bench(); + return result; + } + + public static int Main() + { + bool result = TestBase(); + return (result ? 100 : -1); + } +} +} diff --git a/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDRomber/MDRomber.csproj b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDRomber/MDRomber.csproj new file mode 100644 index 0000000000000..0a051a8d688d8 --- /dev/null +++ b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDRomber/MDRomber.csproj @@ -0,0 +1,15 @@ + + + Exe + + + pdbonly + true + + + + + + $(JitPackagesConfigFileDirectory)benchmark\obj\project.assets.json + + diff --git a/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDSqMtx/MDSqMtx.cs b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDSqMtx/MDSqMtx.cs new file mode 100644 index 0000000000000..669b4c44e47f1 --- /dev/null +++ b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDSqMtx/MDSqMtx.cs @@ -0,0 +1,94 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +using Microsoft.Xunit.Performance; +using System; +using System.Runtime.CompilerServices; +using Xunit; + +[assembly: OptimizeForBenchmarks] + +namespace Benchstone.MDBenchF +{ +public static class MDSqMtx +{ +#if DEBUG + public const int Iterations = 1; +#else + public const int Iterations = 4000; +#endif + + private const int MatrixSize = 40; + + [MethodImpl(MethodImplOptions.NoInlining)] + private static bool Bench() + { + double[,] a = new double[41, 41]; + double[,] c = new double[41, 41]; + + int i, j; + + for (i = 1; i <= MatrixSize; i++) + { + for (j = 1; j <= MatrixSize; j++) + { + a[i,j] = i + j; + } + } + + for (i = 1; i <= Iterations; i++) + { + Inner(a, c, MatrixSize); + } + + if (c[1,1] == 23820.0) + { + return true; + } + else + { + return false; + } + } + + private static void Inner(double[,] a, double[,] c, int n) + { + for (int i = 1; i <= n; i++) + { + for (int j = 1; j <= n; j++) + { + c[i,j] = 0.0; + for (int k = 1; k <= n; k++) + { + c[i,j] = c[i,j] + a[i,k] * a[k,j]; + } + } + } + } + + [Benchmark] + public static void Test() + { + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + Bench(); + } + } + } + + private static bool TestBase() + { + bool result = Bench(); + return result; + } + + public static int Main() + { + bool result = TestBase(); + return (result ? 100 : -1); + } +} +} diff --git a/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDSqMtx/MDSqMtx.csproj b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDSqMtx/MDSqMtx.csproj new file mode 100644 index 0000000000000..0a051a8d688d8 --- /dev/null +++ b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDSqMtx/MDSqMtx.csproj @@ -0,0 +1,15 @@ + + + Exe + + + pdbonly + true + + + + + + $(JitPackagesConfigFileDirectory)benchmark\obj\project.assets.json + + diff --git a/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDAddArray2/MDAddArray2.cs b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDAddArray2/MDAddArray2.cs new file mode 100644 index 0000000000000..9db000dbf1035 --- /dev/null +++ b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDAddArray2/MDAddArray2.cs @@ -0,0 +1,122 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +using Microsoft.Xunit.Performance; +using System; +using System.Runtime.CompilerServices; +using Xunit; + +[assembly: OptimizeForBenchmarks] + +namespace Benchstone.MDBenchI +{ +public static class MDAddArray2 +{ +#if DEBUG + public const int Iterations = 1; +#else + public const int Iterations = 50; +#endif + + private const int Dim = 200; + + private static + void BenchInner1(int[,] a, ref int nn) + { + int n; + int l, m; + n = nn; + for (int i = 1; i <= n; i++) + { + for (int j = (i + 1); j <= n; j++) + { + for (int k = 1; k <= n; k++) + { + l = a[i,k]; + m = a[j,k]; + unchecked + { + a[j,k] = l + m; + } + } + } + } + } + + private static + void BenchInner2(int[,] a, ref int nn) + { + int n; + int l, m; + n = nn; + for (int i = 1; i <= n; i++) + { + for (int j = (i + 1); j <= n; j++) + { + for (int k = 1; k <= n; k++) + { + l = a[k,i]; + m = a[k,j]; + unchecked + { + a[k,j] = l + m; + } + } + } + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static bool Bench(int[,] a) + { + int n = Dim; + for (int i = 1; i <= n; i++) + { + for (int j = 1; j <= n; j++) + { + a[i,j] = i + j; + } + } + + BenchInner1(a, ref n); + n = Dim; + BenchInner2(a, ref n); + + return true; + } + + [Benchmark] + public static void Test() + { + int[,] array = new int[Dim + 1, Dim + 1]; + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 1; i <= Iterations; i++) + { + Bench(array); + } + } + } + } + + private static bool TestBase() + { + int[,] array = new int[Dim + 1, Dim + 1]; + bool result = true; + for (int i = 1; i <= Iterations; i++) + { + result &= Bench(array); + } + return result; + } + + public static int Main() + { + bool result = TestBase(); + return (result ? 100 : -1); + } +} +} diff --git a/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDAddArray2/MDAddArray2.csproj b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDAddArray2/MDAddArray2.csproj new file mode 100644 index 0000000000000..0a051a8d688d8 --- /dev/null +++ b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDAddArray2/MDAddArray2.csproj @@ -0,0 +1,15 @@ + + + Exe + + + pdbonly + true + + + + + + $(JitPackagesConfigFileDirectory)benchmark\obj\project.assets.json + + diff --git a/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDArray2/MDArray2.cs b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDArray2/MDArray2.cs new file mode 100644 index 0000000000000..873f77a5ed6a6 --- /dev/null +++ b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDArray2/MDArray2.cs @@ -0,0 +1,89 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +using Microsoft.Xunit.Performance; +using System; +using System.Runtime.CompilerServices; +using Xunit; + +[assembly: OptimizeForBenchmarks] + +namespace Benchstone.MDBenchI +{ +public static class MDArray2 +{ + +#if DEBUG + public const int Iterations = 1; +#else + public const int Iterations = 500000; +#endif + + static void Initialize(int[,,] s) { + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + for (int k = 0; k < 10; k++) { + s[i,j,k] = (2 * i) - (3 * j) + (5 * k); + } + } + } + } + + static bool VerifyCopy(int[,,] s, int[,,] d) { + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + for (int k = 0; k < 10; k++) { + if (s[i,j,k] != d[i,j,k]) { + return false; + } + } + } + } + + return true; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool Bench(int loop) { + + int[,,] s = new int[10, 10, 10]; + int[,,] d = new int[10, 10, 10]; + + Initialize(s); + + for (; loop != 0; loop--) { + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + for (int k = 0; k < 10; k++) { + d[i,j,k] = s[i,j,k]; + } + } + } + } + + bool result = VerifyCopy(s, d); + + return result; + } + + [Benchmark] + public static void Test() { + foreach (var iteration in Benchmark.Iterations) { + using (iteration.StartMeasurement()) { + Bench(Iterations); + } + } + } + + static bool TestBase() { + bool result = Bench(Iterations); + return result; + } + + public static int Main() { + bool result = TestBase(); + return (result ? 100 : -1); + } +} +} diff --git a/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDArray2/MDArray2.csproj b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDArray2/MDArray2.csproj new file mode 100644 index 0000000000000..0a051a8d688d8 --- /dev/null +++ b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDArray2/MDArray2.csproj @@ -0,0 +1,15 @@ + + + Exe + + + pdbonly + true + + + + + + $(JitPackagesConfigFileDirectory)benchmark\obj\project.assets.json + + diff --git a/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDLogicArray/MDLogicArray.cs b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDLogicArray/MDLogicArray.cs new file mode 100644 index 0000000000000..7f0ac97232e6f --- /dev/null +++ b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDLogicArray/MDLogicArray.cs @@ -0,0 +1,88 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +using Microsoft.Xunit.Performance; +using System; +using System.Runtime.CompilerServices; +using Xunit; + +[assembly: OptimizeForBenchmarks] + +namespace Benchstone.MDBenchI +{ +public static class MDLogicArray +{ + +#if DEBUG + public const int Iterations = 1; +#else + public const int Iterations = 3000; +#endif + + struct Workarea + { + public int X; + public int[,] A; + } + + static bool Inner(ref Workarea cmn) { + int i, j, k; + cmn.X = 0; + for (i = 1; i <= 50; i++) { + for (j = 1; j <= 50; j++) { + cmn.A[i,j] = 1; + } + } + for (k = 1; k <= 50; k++) { + for (j = 1; j <= 50; j++) { + i = 1; + do { + cmn.X = cmn.X | cmn.A[i,j] & cmn.A[i + 1,k]; + i = i + 2; + } while (i <= 50); + } + } + if (cmn.X != 1) { + return false; + } + else { + return true; + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool Bench() { + Workarea cmn = new Workarea(); + cmn.X = 0; + cmn.A = new int[51, 51]; + for (int n = 1; n <= Iterations; n++) { + bool result = Inner(ref cmn); + if (!result) { + return false; + } + } + + return true; + } + + [Benchmark] + public static void Test() { + foreach (var iteration in Benchmark.Iterations) { + using (iteration.StartMeasurement()) { + Bench(); + } + } + } + + static bool TestBase() { + bool result = Bench(); + return result; + } + + public static int Main() { + bool result = TestBase(); + return (result ? 100 : -1); + } +} +} diff --git a/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDLogicArray/MDLogicArray.csproj b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDLogicArray/MDLogicArray.csproj new file mode 100644 index 0000000000000..0a051a8d688d8 --- /dev/null +++ b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDLogicArray/MDLogicArray.csproj @@ -0,0 +1,15 @@ + + + Exe + + + pdbonly + true + + + + + + $(JitPackagesConfigFileDirectory)benchmark\obj\project.assets.json + + diff --git a/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDMidpoint/MDMidpoint.cs b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDMidpoint/MDMidpoint.cs new file mode 100644 index 0000000000000..b46ce147b35a8 --- /dev/null +++ b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDMidpoint/MDMidpoint.cs @@ -0,0 +1,97 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +using Microsoft.Xunit.Performance; +using System; +using System.Runtime.CompilerServices; +using Xunit; + +[assembly: OptimizeForBenchmarks] + +namespace Benchstone.MDBenchI +{ +public static class MDMidpoint +{ + +#if DEBUG + public const int Iterations = 1; +#else + public const int Iterations = 70000; +#endif + + static int Inner(ref int x, ref int y, ref int z) { + int mid; + + if (x < y) { + if (y < z) { + mid = y; + } + else { + if (x < z) { + mid = z; + } + else { + mid = x; + } + } + } + else { + if (x < z) { + mid = x; + } + else { + if (y < z) { + mid = z; + } + else { + mid = y; + } + } + } + + return (mid); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool Bench() { + int[,] a = new int[2001, 4]; + int[] mid = new int[2001]; + int j = 99999; + + for (int i = 1; i <= 2000; i++) { + a[i,1] = j & 32767; + a[i,2] = (j + 11111) & 32767; + a[i,3] = (j + 22222) & 32767; + j = j + 33333; + } + + for (int k = 1; k <= Iterations; k++) { + for (int l = 1; l <= 2000; l++) { + mid[l] = Inner(ref a[l,1], ref a[l,2], ref a[l,3]); + } + } + + return (mid[2000] == 17018); + } + + [Benchmark] + public static void Test() { + foreach (var iteration in Benchmark.Iterations) { + using (iteration.StartMeasurement()) { + Bench(); + } + } + } + + static bool TestBase() { + bool result = Bench(); + return result; + } + + public static int Main() { + bool result = TestBase(); + return (result ? 100 : -1); + } +} +} diff --git a/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDMidpoint/MDMidpoint.csproj b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDMidpoint/MDMidpoint.csproj new file mode 100644 index 0000000000000..0a051a8d688d8 --- /dev/null +++ b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDMidpoint/MDMidpoint.csproj @@ -0,0 +1,15 @@ + + + Exe + + + pdbonly + true + + + + + + $(JitPackagesConfigFileDirectory)benchmark\obj\project.assets.json + + diff --git a/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDMulMatrix/MDMulMatrix.cs b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDMulMatrix/MDMulMatrix.cs new file mode 100644 index 0000000000000..6a1f829edfbb9 --- /dev/null +++ b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDMulMatrix/MDMulMatrix.cs @@ -0,0 +1,133 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +using Microsoft.Xunit.Performance; +using System; +using System.Runtime.CompilerServices; +using Xunit; + +[assembly: OptimizeForBenchmarks] + +namespace Benchstone.MDBenchI +{ +public static class MDMulMatrix +{ + +#if DEBUG + public const int Iterations = 1; +#else + public const int Iterations = 100; +#endif + + const int Size = 75; + static volatile object VolatileObject; + + static void Escape(object obj) { + VolatileObject = obj; + } + + static void Inner(int[,] a, int[,] b, int[,] c) { + + int i, j, k, l; + + // setup + for (j = 0; j < Size; j++) { + for (i = 0; i < Size; i++) { + a[i,j] = i; + b[i,j] = 2 * j; + c[i,j] = a[i,j] + b[i,j]; + } + } + + // jkl + for (j = 0; j < Size; j++) { + for (k = 0; k < Size; k++) { + for (l = 0; l < Size; l++) { + c[j,k] += a[j,l] * b[l,k]; + } + } + } + + // jlk + for (j = 0; j < Size; j++) { + for (l = 0; l < Size; l++) { + for (k = 0; k < Size; k++) { + c[j,k] += a[j,l] * b[l,k]; + } + } + } + + // kjl + for (k = 0; k < Size; k++) { + for (j = 0; j < Size; j++) { + for (l = 0; l < Size; l++) { + c[j,k] += a[j,l] * b[l,k]; + } + } + } + + // klj + for (k = 0; k < Size; k++) { + for (l = 0; l < Size; l++) { + for (j = 0; j < Size; j++) { + c[j,k] += a[j,l] * b[l,k]; + } + } + } + + // ljk + for (l = 0; l < Size; l++) { + for (j = 0; j < Size; j++) { + for (k = 0; k < Size; k++) { + c[j,k] += a[j,l] * b[l,k]; + } + } + } + + // lkj + for (l = 0; l < Size; l++) { + for (k = 0; k < Size; k++) { + for (j = 0; j < Size; j++) { + c[j,k] += a[j,l] * b[l,k]; + } + } + } + + return; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool Bench() { + int[,] a = new int[Size, Size]; + int[,] b = new int[Size, Size]; + int[,] c = new int[Size, Size]; + + for (int i = 0; i < Iterations; ++i) { + Inner(a, b, c); + } + + Escape(c); + return true; + } + + [Benchmark] + public static void Test() { + foreach (var iteration in Benchmark.Iterations) { + using (iteration.StartMeasurement()) { + Bench(); + } + } + } + + static bool TestBase() { + bool result = Bench(); + return result; + } + + public static int Main() { + bool result = TestBase(); + return (result ? 100 : -1); + } +} +} diff --git a/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDMulMatrix/MDMulMatrix.csproj b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDMulMatrix/MDMulMatrix.csproj new file mode 100644 index 0000000000000..0a051a8d688d8 --- /dev/null +++ b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDMulMatrix/MDMulMatrix.csproj @@ -0,0 +1,15 @@ + + + Exe + + + pdbonly + true + + + + + + $(JitPackagesConfigFileDirectory)benchmark\obj\project.assets.json + + diff --git a/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDNDhrystone/MDNDhrystone.cs b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDNDhrystone/MDNDhrystone.cs new file mode 100644 index 0000000000000..73fcde8e4eb7b --- /dev/null +++ b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDNDhrystone/MDNDhrystone.cs @@ -0,0 +1,279 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// Adapted from +// +// Dhrystone: a synthetic systems programming benchmark +// Reinhold P. Weicker +// Communications of the ACM, Volume 27 Issue 10, Oct 1984, Pages 1013-1030 + +using Microsoft.Xunit.Performance; +using System; +using System.Runtime.CompilerServices; +using Xunit; + +[assembly: OptimizeForBenchmarks] + +namespace Benchstone.MDBenchI +{ +public static class MDNDhrystone +{ + +#if DEBUG + public const int Iterations = 1; +#else + public const int Iterations = 7000000; +#endif + + enum Enumeration + { + Ident1 = 1, Ident2, Ident3, Ident4, Ident5 + } + + sealed class Record + { + public Record PtrComp; + public Enumeration Discr; + public Enumeration EnumComp; + public int IntComp; + public char[] StringComp; + } + + static int s_intGlob; + static bool s_boolGlob; + static char s_char1Glob; + static char s_char2Glob; + static int[] m_array1Glob = new int[51]; + static int[,] m_array2Glob; + static Record m_ptrGlb = new Record(); + static Record m_ptrGlbNext = new Record(); + static char[] m_string1Loc; + static char[] m_string2Loc; + + static void Proc0() { + int intLoc1; + int intLoc2; + int intLoc3 = 0; + Enumeration enumLoc; + + int i; /* modification */ + + m_ptrGlb.PtrComp = m_ptrGlbNext; + m_ptrGlb.Discr = Enumeration.Ident1; + m_ptrGlb.EnumComp = Enumeration.Ident3; + m_ptrGlb.IntComp = 40; + m_ptrGlb.StringComp = "DHRYSTONE PROGRAM, SOME STRING".ToCharArray(); + m_string1Loc = "DHRYSTONE PROGRAM, 1'ST STRING".ToCharArray(); + m_array2Glob[8,7] = 10; /* Was missing in published program */ + + for (i = 0; i < Iterations; ++i) { + Proc5(); + Proc4(); + intLoc1 = 2; + intLoc2 = 3; + m_string2Loc = "DHRYSTONE PROGRAM, 2'ND STRING".ToCharArray(); + enumLoc = Enumeration.Ident2; + s_boolGlob = !Func2(m_string1Loc, m_string2Loc); + while (intLoc1 < intLoc2) { + intLoc3 = 5 * intLoc1 - intLoc2; + Proc7(intLoc1, intLoc2, ref intLoc3); + ++intLoc1; + } + Proc8(m_array1Glob, m_array2Glob, intLoc1, intLoc3); + Proc1(ref m_ptrGlb); + for (char charIndex = 'A'; charIndex <= s_char2Glob; ++charIndex) { + if (enumLoc == Func1(charIndex, 'C')) { + Proc6(Enumeration.Ident1, ref enumLoc); + } + } + intLoc3 = intLoc2 * intLoc1; + intLoc2 = intLoc3 / intLoc1; + intLoc2 = 7 * (intLoc3 - intLoc2) - intLoc1; + Proc2(ref intLoc1); + } + } + + static void Proc1(ref Record ptrParIn) { + ptrParIn.PtrComp = m_ptrGlb; + ptrParIn.IntComp = 5; + ptrParIn.PtrComp.IntComp = ptrParIn.IntComp; + ptrParIn.PtrComp.PtrComp = ptrParIn.PtrComp; + Proc3(ref ptrParIn.PtrComp.PtrComp); + if (ptrParIn.PtrComp.Discr == Enumeration.Ident1) { + ptrParIn.PtrComp.IntComp = 6; + Proc6(ptrParIn.EnumComp, ref ptrParIn.PtrComp.EnumComp); + ptrParIn.PtrComp.PtrComp = m_ptrGlb.PtrComp; + Proc7(ptrParIn.PtrComp.IntComp, 10, ref ptrParIn.PtrComp.IntComp); + } + else { + ptrParIn = ptrParIn.PtrComp; + } + } + + static void Proc2(ref int intParIO) { + int intLoc; + Enumeration enumLoc = Enumeration.Ident2; + intLoc = intParIO + 10; + + for (;;) { + if (s_char1Glob == 'A') { + --intLoc; + intParIO = intLoc - s_intGlob; + enumLoc = Enumeration.Ident1; + } + if (enumLoc == Enumeration.Ident1) { + break; + } + } + } + + static void Proc3(ref Record ptrParOut) { + if (m_ptrGlb != null) { + ptrParOut = m_ptrGlb.PtrComp; + } + else { + s_intGlob = 100; + } + + Proc7(10, s_intGlob, ref m_ptrGlb.IntComp); + } + + static void Proc4() { + bool boolLoc; + boolLoc = s_char1Glob == 'A'; + boolLoc |= s_boolGlob; + s_char2Glob = 'B'; + } + + static void Proc5() { + s_char1Glob = 'A'; + s_boolGlob = false; + } + + static void Proc6(Enumeration enumParIn, ref Enumeration enumParOut) { + enumParOut = enumParIn; + if (!Func3(enumParIn)) { + enumParOut = Enumeration.Ident4; + } + + switch (enumParIn) { + case Enumeration.Ident1: + enumParOut = Enumeration.Ident1; + break; + case Enumeration.Ident2: + if (s_intGlob > 100) { + enumParOut = Enumeration.Ident1; + } + else { + enumParOut = Enumeration.Ident4; + } + break; + case Enumeration.Ident3: + enumParOut = Enumeration.Ident2; + break; + case Enumeration.Ident4: + break; + case Enumeration.Ident5: + enumParOut = Enumeration.Ident3; + break; + } + } + + static void Proc7(int intParI1, int intParI2, ref int intParOut) { + int intLoc; + intLoc = intParI1 + 2; + intParOut = intParI2 + intLoc; + } + + static void Proc8(int[] array1Par, int[,] array2Par, int intParI1, int intParI2) { + int intLoc; + intLoc = intParI1 + 5; + array1Par[intLoc] = intParI2; + array1Par[intLoc + 1] = array1Par[intLoc]; + array1Par[intLoc + 30] = intLoc; + for (int intIndex = intLoc; intIndex <= (intLoc + 1); ++intIndex) { + array2Par[intLoc,intIndex] = intLoc; + } + ++array2Par[intLoc,intLoc - 1]; + array2Par[intLoc + 20,intLoc] = array1Par[intLoc]; + s_intGlob = 5; + } + + static Enumeration Func1(char charPar1, char charPar2) { + char charLoc1; + char charLoc2; + charLoc1 = charPar1; + charLoc2 = charLoc1; + if (charLoc2 != charPar2) { + return (Enumeration.Ident1); + } + else { + return (Enumeration.Ident2); + } + } + + static bool Func2(char[] strParI1, char[] strParI2) { + int intLoc; + char charLoc = '\0'; + intLoc = 1; + while (intLoc <= 1) { + if (Func1(strParI1[intLoc], strParI2[intLoc + 1]) == Enumeration.Ident1) { + charLoc = 'A'; + ++intLoc; + } + } + if (charLoc >= 'W' && charLoc <= 'Z') { + intLoc = 7; + } + if (charLoc == 'X') { + return true; + } + else { + for (int i = 0; i < 30; i++) { + if (strParI1[i] > strParI2[i]) { + intLoc += 7; + return true; + } + } + + return false; + } + } + + static bool Func3(Enumeration enumParIn) { + Enumeration enumLoc; + enumLoc = enumParIn; + if (enumLoc == Enumeration.Ident3) { + return true; + } + + return false; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool Bench() { + m_array2Glob = new int[51, 51]; + Proc0(); + return true; + } + + [Benchmark] + public static void Test() { + foreach (var iteration in Benchmark.Iterations) { + using (iteration.StartMeasurement()) { + Bench(); + } + } + } + + static bool TestBase() { + bool result = Bench(); + return result; + } + + public static int Main() { + bool result = TestBase(); + return (result ? 100 : -1); + } +} +} diff --git a/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDNDhrystone/MDNDhrystone.csproj b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDNDhrystone/MDNDhrystone.csproj new file mode 100644 index 0000000000000..0a051a8d688d8 --- /dev/null +++ b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDNDhrystone/MDNDhrystone.csproj @@ -0,0 +1,15 @@ + + + Exe + + + pdbonly + true + + + + + + $(JitPackagesConfigFileDirectory)benchmark\obj\project.assets.json + + diff --git a/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDPuzzle/MDPuzzle.cs b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDPuzzle/MDPuzzle.cs new file mode 100644 index 0000000000000..563c8cb0d83db --- /dev/null +++ b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDPuzzle/MDPuzzle.cs @@ -0,0 +1,383 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +using Microsoft.Xunit.Performance; +using System; +using System.Runtime.CompilerServices; +using Xunit; + +[assembly: OptimizeForBenchmarks] + +namespace Benchstone.MDBenchI +{ +public class MDPuzzle +{ +#if DEBUG + public const int Iterations = 1; +#else + public const int Iterations = 400; +#endif + + private const int PuzzleSize = 511; + private const int ClassMax = 3; + private const int TypeMax = 12; + private const int D = 8; + + private int[] _pieceCount = new int[ClassMax + 1]; + private int[] _class = new int[TypeMax + 1]; + private int[] _pieceMax = new int[TypeMax + 1]; + private bool[] _puzzle = new bool[PuzzleSize + 1]; + private bool[,] _p; + private int _count; + + private bool Fit(int i, int j) + { + for (int k = 0; k <= _pieceMax[i]; k++) + { + if (_p[i,k]) + { + if (_puzzle[j + k]) + { + return false; + } + } + } + + return true; + } + + private int Place(int i, int j) + { + int k; + for (k = 0; k <= _pieceMax[i]; k++) + { + if (_p[i,k]) + { + _puzzle[j + k] = true; + } + } + + _pieceCount[_class[i]] = _pieceCount[_class[i]] - 1; + + for (k = j; k <= PuzzleSize; k++) + { + if (!_puzzle[k]) + { + return k; + } + } + + return 0; + } + + private void RemoveLocal(int i, int j) + { + for (int k = 0; k <= _pieceMax[i]; k++) + { + if (_p[i,k]) + { + _puzzle[j + k] = false; + } + } + + _pieceCount[_class[i]] = _pieceCount[_class[i]] + 1; + } + + private bool Trial(int j) + { + for (int i = 0; i <= TypeMax; i++) + { + if (_pieceCount[_class[i]] != 0) + { + if (Fit(i, j)) + { + int k = Place(i, j); + if (Trial(k) || (k == 0)) + { + _count = _count + 1; + return true; + } + else + { + RemoveLocal(i, j); + } + } + } + } + + _count = _count + 1; + return false; + } + + private bool DoIt() + { + int i, j, k, m, n; + + for (m = 0; m <= PuzzleSize; m++) + { + _puzzle[m] = true; + } + + for (i = 1; i <= 5; i++) + { + for (j = 1; j <= 5; j++) + { + for (k = 1; k <= 5; k++) + { + _puzzle[i + D * (j + D * k)] = false; + } + } + } + + for (i = 0; i <= TypeMax; i++) + { + for (m = 0; m <= PuzzleSize; m++) + { + _p[i,m] = false; + } + } + + for (i = 0; i <= 3; i++) + { + for (j = 0; j <= 1; j++) + { + for (k = 0; k <= 0; k++) + { + _p[0,i + D * (j + D * k)] = true; + } + } + } + + _class[0] = 0; + _pieceMax[0] = 3 + D * 1 + D * D * 0; + + for (i = 0; i <= 1; i++) + { + for (j = 0; j <= 0; j++) + { + for (k = 0; k <= 3; k++) + { + _p[1,i + D * (j + D * k)] = true; + } + } + } + + _class[1] = 0; + _pieceMax[1] = 1 + D * 0 + D * D * 3; + + for (i = 0; i <= 0; i++) + { + for (j = 0; j <= 3; j++) + { + for (k = 0; k <= 1; k++) + { + _p[2,i + D * (j + D * k)] = true; + } + } + } + _class[2] = 0; + _pieceMax[2] = 0 + D * 3 + D * D * 1; + + for (i = 0; i <= 1; i++) + { + for (j = 0; j <= 3; j++) + { + for (k = 0; k <= 0; k++) + { + _p[3,i + D * (j + D * k)] = true; + } + } + } + + _class[3] = 0; + _pieceMax[3] = 1 + D * 3 + D * D * 0; + + for (i = 0; i <= 3; i++) + { + for (j = 0; j <= 0; j++) + { + for (k = 0; k <= 1; k++) + { + _p[4,i + D * (j + D * k)] = true; + } + } + } + + _class[4] = 0; + _pieceMax[4] = 3 + D * 0 + D * D * 1; + + for (i = 0; i <= 0; i++) + { + for (j = 0; j <= 1; j++) + { + for (k = 0; k <= 3; k++) + { + _p[5,i + D * (j + D * k)] = true; + } + } + } + + _class[5] = 0; + _pieceMax[5] = 0 + D * 1 + D * D * 3; + + for (i = 0; i <= 2; i++) + { + for (j = 0; j <= 0; j++) + { + for (k = 0; k <= 0; k++) + { + _p[6,i + D * (j + D * k)] = true; + } + } + } + + _class[6] = 1; + _pieceMax[6] = 2 + D * 0 + D * D * 0; + + for (i = 0; i <= 0; i++) + { + for (j = 0; j <= 2; j++) + { + for (k = 0; k <= 0; k++) + { + _p[7,i + D * (j + D * k)] = true; + } + } + } + + _class[7] = 1; + _pieceMax[7] = 0 + D * 2 + D * D * 0; + + for (i = 0; i <= 0; i++) + { + for (j = 0; j <= 0; j++) + { + for (k = 0; k <= 2; k++) + { + _p[8,i + D * (j + D * k)] = true; + } + } + } + + _class[8] = 1; + _pieceMax[8] = 0 + D * 0 + D * D * 2; + + for (i = 0; i <= 1; i++) + { + for (j = 0; j <= 1; j++) + { + for (k = 0; k <= 0; k++) + { + _p[9,i + D * (j + D * k)] = true; + } + } + } + _class[9] = 2; + _pieceMax[9] = 1 + D * 1 + D * D * 0; + + for (i = 0; i <= 1; i++) + { + for (j = 0; j <= 0; j++) + { + for (k = 0; k <= 1; k++) + { + _p[10,i + D * (j + D * k)] = true; + } + } + } + + _class[10] = 2; + _pieceMax[10] = 1 + D * 0 + D * D * 1; + + for (i = 0; i <= 0; i++) + { + for (j = 0; j <= 1; j++) + { + for (k = 0; k <= 1; k++) + { + _p[11,i + D * (j + D * k)] = true; + } + } + } + + _class[11] = 2; + _pieceMax[11] = 0 + D * 1 + D * D * 1; + + for (i = 0; i <= 1; i++) + { + for (j = 0; j <= 1; j++) + { + for (k = 0; k <= 1; k++) + { + _p[12,i + D * (j + D * k)] = true; + } + } + } + + _class[12] = 3; + _pieceMax[12] = 1 + D * 1 + D * D * 1; + _pieceCount[0] = 13; + _pieceCount[1] = 3; + _pieceCount[2] = 1; + _pieceCount[3] = 1; + m = 1 + D * (1 + D * 1); + _count = 0; + + bool result = true; + + if (Fit(0, m)) + { + n = Place(0, m); + result = Trial(n); + } + else + { + result = false; + } + + return result; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private bool Bench() + { + _p = new bool[TypeMax + 1, PuzzleSize + 1]; + + bool result = true; + + for (int i = 0; i < Iterations; ++i) + { + result &= DoIt(); + } + + return result; + } + + [Benchmark] + public static void Test() + { + MDPuzzle P = new MDPuzzle(); + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + P.Bench(); + } + } + } + + private static bool TestBase() + { + MDPuzzle P = new MDPuzzle(); + bool result = P.Bench(); + return result; + } + + public static int Main() + { + bool result = TestBase(); + return (result ? 100 : -1); + } +} +} diff --git a/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDPuzzle/MDPuzzle.csproj b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDPuzzle/MDPuzzle.csproj new file mode 100644 index 0000000000000..0a051a8d688d8 --- /dev/null +++ b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDPuzzle/MDPuzzle.csproj @@ -0,0 +1,15 @@ + + + Exe + + + pdbonly + true + + + + + + $(JitPackagesConfigFileDirectory)benchmark\obj\project.assets.json + + diff --git a/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDXposMatrix/MDXposMatrix.cs b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDXposMatrix/MDXposMatrix.cs new file mode 100644 index 0000000000000..11743bbebbc1e --- /dev/null +++ b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDXposMatrix/MDXposMatrix.cs @@ -0,0 +1,83 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +using Microsoft.Xunit.Performance; +using System; +using System.Runtime.CompilerServices; +using Xunit; + +[assembly: OptimizeForBenchmarks] + +namespace Benchstone.MDBenchI +{ +public static class MDXposMatrix +{ + public const int ArraySize = 100; + +#if DEBUG + public const int Iterations = 1; +#else + public const int Iterations = 25000; +#endif + + static void Inner(int[,] x, int n) { + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= n; j++) { + int t = x[i,j]; + x[i,j] = x[j,i]; + x[j,i] = t; + } + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool Bench(int[,] matrix) { + + int n = ArraySize; + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= n; j++) { + matrix[i,j] = 1; + } + } + + if (matrix[n,n] != 1) { + return false; + } + + Inner(matrix, n); + + if (matrix[n,n] != 1) { + return false; + } + + return true; + } + + [Benchmark] + public static void Test() { + int[,] matrix = new int[ArraySize + 1, ArraySize + 1]; + foreach (var iteration in Benchmark.Iterations) { + using (iteration.StartMeasurement()) { + for (int i = 0; i < Iterations; i++) { + Bench(matrix); + } + } + } + } + + static bool TestBase() { + int[,] matrix = new int[ArraySize + 1, ArraySize + 1]; + bool result = true; + for (int i = 0; i < Iterations; i++) { + result &= Bench(matrix); + } + return result; + } + + public static int Main() { + bool result = TestBase(); + return (result ? 100 : -1); + } +} +} diff --git a/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDXposMatrix/MDXposMatrix.csproj b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDXposMatrix/MDXposMatrix.csproj new file mode 100644 index 0000000000000..0a051a8d688d8 --- /dev/null +++ b/src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDXposMatrix/MDXposMatrix.csproj @@ -0,0 +1,15 @@ + + + Exe + + + pdbonly + true + + + + + + $(JitPackagesConfigFileDirectory)benchmark\obj\project.assets.json + + From 383a479a0b95fbaf3b359e1d0565a68447a86856 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Tue, 12 Oct 2021 11:15:54 +0300 Subject: [PATCH 043/106] [mono][interp] Fix short branches (#58806) * [interp] Fix short branches In interp, long branches have 4 byte signed offset while short branches have a 2 byte signed offset. Ideally we would want to always emit short branches if possible due to improved performance. The problem is that we emit a short or long branch early in the codegen process, there is no way to know whether a branch is long or short at that time and no further computations were done later to determine whether the branch is really long or short. Before this commit we arbitrarily decided that all branches in methods with IL size of less than 25000 are short and, in some cases, completely ignored that long branches actually happen in practice. This commit makes it such that we always emit long opcodes at the beginning of the codegen process, also serving as a simplification since optimizations operating on IR code don't need to care about both short and long versions of branches. Later on, we will end up converting all these long branches to short branches when emitting the final method code. We achieve this by doing a quick preliminary code iteration and computing conservative native offsets (assuming all branches are long). Since the final code will be equal in size or smaller, we have the guarantee that if a branch is short between the conservative offsets, it will surely be short also in the final code. With this approach we guarantee correctness and we can fail to shorten only a negligible amount of branches. Since the super instruction pass generates some branching instructions that are supported only for the short version, we need to run a computation of conservative offsets beforehand. * Re-enable test suite --- .../Compiler/CSharpCodeGeneratorTests.cs | 1 - src/mono/mono/mini/interp/mintops.h | 3 + src/mono/mono/mini/interp/transform.c | 322 ++++++++++-------- src/mono/mono/mini/interp/transform.h | 9 + 4 files changed, 188 insertions(+), 147 deletions(-) diff --git a/src/libraries/System.CodeDom/tests/System/CodeDom/Compiler/CSharpCodeGeneratorTests.cs b/src/libraries/System.CodeDom/tests/System/CodeDom/Compiler/CSharpCodeGeneratorTests.cs index 8b9f94b0dbc7b..0256638f5f6a8 100644 --- a/src/libraries/System.CodeDom/tests/System/CodeDom/Compiler/CSharpCodeGeneratorTests.cs +++ b/src/libraries/System.CodeDom/tests/System/CodeDom/Compiler/CSharpCodeGeneratorTests.cs @@ -9,7 +9,6 @@ namespace System.CodeDom.Compiler.Tests { - [ActiveIssue("https://github.com/dotnet/runtime/issues/57363", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoInterpreter))] public class CSharpCodeGeneratorTests { private static IEnumerable Identifier_TestData() diff --git a/src/mono/mono/mini/interp/mintops.h b/src/mono/mono/mini/interp/mintops.h index 6de5e8c4ec0a0..e1a381f0a5dda 100644 --- a/src/mono/mono/mini/interp/mintops.h +++ b/src/mono/mono/mini/interp/mintops.h @@ -59,10 +59,13 @@ typedef enum { #define MINT_SWITCH_LEN(n) (4 + (n) * 2) +#define MINT_IS_NOP(op) ((op) == MINT_NOP || (op) == MINT_DEF || (op) == MINT_DUMMY_USE || (op) == MINT_IL_SEQ_POINT) #define MINT_IS_MOV(op) ((op) >= MINT_MOV_I1 && (op) <= MINT_MOV_VT) +#define MINT_IS_UNCONDITIONAL_BRANCH(op) ((op) >= MINT_BR && (op) <= MINT_CALL_HANDLER_S) #define MINT_IS_CONDITIONAL_BRANCH(op) ((op) >= MINT_BRFALSE_I4 && (op) <= MINT_BLT_UN_R8_S) #define MINT_IS_UNOP_CONDITIONAL_BRANCH(op) ((op) >= MINT_BRFALSE_I4 && (op) <= MINT_BRTRUE_R8_S) #define MINT_IS_BINOP_CONDITIONAL_BRANCH(op) ((op) >= MINT_BEQ_I4 && (op) <= MINT_BLT_UN_R8_S) +#define MINT_IS_SUPER_BRANCH(op) ((op) >= MINT_BRFALSE_I4_SP && (op) <= MINT_BLT_UN_I8_IMM_SP) #define MINT_IS_CALL(op) ((op) >= MINT_CALL && (op) <= MINT_JIT_CALL) #define MINT_IS_PATCHABLE_CALL(op) ((op) >= MINT_CALL && (op) <= MINT_VCALL) #define MINT_IS_LDC_I4(op) ((op) >= MINT_LDC_I4_M1 && (op) <= MINT_LDC_I4) diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 0196a402d6d0c..579245bbbe4c8 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -582,7 +582,7 @@ interp_merge_bblocks (TransformData *td, InterpBasicBlock *bb, InterpBasicBlock if (bb->last_ins) { InterpInst *last_ins = (bb->last_ins->opcode != MINT_NOP) ? bb->last_ins : interp_prev_ins (bb->last_ins); if (last_ins) { - if (last_ins->opcode == MINT_BR || last_ins->opcode == MINT_BR_S) { + if (last_ins->opcode == MINT_BR) { g_assert (last_ins->info.target_bb == bbadd); interp_clear_ins (last_ins); } else if (last_ins->opcode == MINT_SWITCH) { @@ -779,9 +779,8 @@ init_bb_stack_state (TransformData *td, InterpBasicBlock *bb) } static void -handle_branch (TransformData *td, int short_op, int long_op, int offset) +handle_branch (TransformData *td, int long_op, int offset) { - int shorten_branch = 0; int target = td->ip + offset - td->il_code; if (target < 0 || target >= td->code_size) g_assert_not_reached (); @@ -794,7 +793,7 @@ handle_branch (TransformData *td, int short_op, int long_op, int offset) InterpBasicBlock *target_bb = td->offset_to_bb [target]; g_assert (target_bb); - if (short_op == MINT_LEAVE_S || short_op == MINT_LEAVE_S_CHECK) + if (long_op == MINT_LEAVE || long_op == MINT_LEAVE_CHECK) target_bb->eh_block = TRUE; fixup_newbb_stack_locals (td, target_bb); @@ -803,16 +802,8 @@ handle_branch (TransformData *td, int short_op, int long_op, int offset) interp_link_bblocks (td, td->cbb, target_bb); - if (td->header->code_size <= 25000) /* FIX to be precise somehow? */ - shorten_branch = 1; - - if (shorten_branch) { - interp_add_ins (td, short_op); - td->last_ins->info.target_bb = target_bb; - } else { - interp_add_ins (td, long_op); - td->last_ins->info.target_bb = target_bb; - } + interp_add_ins (td, long_op); + td->last_ins->info.target_bb = target_bb; } static void @@ -820,11 +811,10 @@ one_arg_branch(TransformData *td, int mint_op, int offset, int inst_size) { int type = td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP ? STACK_TYPE_I : td->sp [-1].type; int long_op = mint_op + type - STACK_TYPE_I4; - int short_op = long_op + MINT_BRFALSE_I4_S - MINT_BRFALSE_I4; CHECK_STACK(td, 1); --td->sp; if (offset) { - handle_branch (td, short_op, long_op, offset + inst_size); + handle_branch (td, long_op, offset + inst_size); interp_ins_set_sreg (td->last_ins, td->sp->local); } else { interp_add_ins (td, MINT_NOP); @@ -870,10 +860,9 @@ two_arg_branch(TransformData *td, int mint_op, int offset, int inst_size) } int long_op = mint_op + type1 - STACK_TYPE_I4; - int short_op = long_op + MINT_BEQ_I4_S - MINT_BEQ_I4; td->sp -= 2; if (offset) { - handle_branch (td, short_op, long_op, offset + inst_size); + handle_branch (td, long_op, offset + inst_size); interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); } else { interp_add_ins (td, MINT_NOP); @@ -3322,7 +3311,7 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target for (i = csignature->param_count - 1 + !!csignature->hasthis; i >= 0; --i) store_arg (td, i); - interp_add_ins (td, MINT_BR_S); + interp_add_ins (td, MINT_BR); // We are branching to the beginning of the method td->last_ins->info.target_bb = td->entry_bb; int in_offset = td->ip - td->il_code; @@ -4957,7 +4946,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, if (inlining) { td->ip++; fixup_newbb_stack_locals (td, exit_bb); - interp_add_ins (td, MINT_BR_S); + interp_add_ins (td, MINT_BR); td->last_ins->info.target_bb = exit_bb; init_bb_stack_state (td, exit_bb); interp_link_bblocks (td, td->cbb, exit_bb); @@ -5021,7 +5010,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, case CEE_BR: { int offset = read32 (td->ip + 1); if (offset) { - handle_branch (td, MINT_BR_S, MINT_BR, 5 + offset); + handle_branch (td, MINT_BR, 5 + offset); link_bblocks = FALSE; } td->ip += 5; @@ -5030,7 +5019,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, case CEE_BR_S: { int offset = (gint8)td->ip [1]; if (offset) { - handle_branch (td, MINT_BR_S, MINT_BR, 2 + (gint8)td->ip [1]); + handle_branch (td, MINT_BR, 2 + (gint8)td->ip [1]); link_bblocks = FALSE; } td->ip += 2; @@ -6905,20 +6894,16 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, continue; if (MONO_OFFSET_IN_CLAUSE (clause, (td->ip - header->code)) && (!MONO_OFFSET_IN_CLAUSE (clause, (target_offset + in_offset)))) { - handle_branch (td, MINT_CALL_HANDLER_S, MINT_CALL_HANDLER, clause->handler_offset - in_offset); - // FIXME We need new IR to get rid of _S ugliness - if (td->last_ins->opcode == MINT_CALL_HANDLER_S) - td->last_ins->data [1] = i; - else - td->last_ins->data [2] = i; + handle_branch (td, MINT_CALL_HANDLER, clause->handler_offset - in_offset); + td->last_ins->data [2] = i; } } if (td->clause_indexes [in_offset] != -1) { /* LEAVE instructions in catch clauses need to check for abort exceptions */ - handle_branch (td, MINT_LEAVE_S_CHECK, MINT_LEAVE_CHECK, target_offset); + handle_branch (td, MINT_LEAVE_CHECK, target_offset); } else { - handle_branch (td, MINT_LEAVE_S, MINT_LEAVE, target_offset); + handle_branch (td, MINT_LEAVE, target_offset); } if (*td->ip == CEE_LEAVE) @@ -7642,6 +7627,60 @@ get_inst_length (InterpInst *ins) return mono_interp_oplen [ins->opcode]; } +static int +compute_native_offset_estimates (TransformData *td) +{ + InterpBasicBlock *bb; + int noe = 0; + for (bb = td->entry_bb; bb != NULL; bb = bb->next_bb) { + InterpInst *ins; + bb->native_offset_estimate = noe; + for (ins = bb->first_ins; ins != NULL; ins = ins->next) { + int opcode = ins->opcode; + // Skip dummy opcodes for more precise offset computation + if (MINT_IS_NOP (opcode)) + continue; + noe += get_inst_length (ins); + } + } + return noe; +} + +static gboolean +is_short_offset (int src_offset, int dest_offset) +{ + int diff = dest_offset - src_offset; + if (diff >= G_MININT16 && diff <= G_MAXINT16) + return TRUE; + return FALSE; +} + +static int +get_short_brop (int opcode) +{ + if (MINT_IS_UNCONDITIONAL_BRANCH (opcode)) { + if (opcode == MINT_BR) + return MINT_BR_S; + else if (opcode == MINT_LEAVE) + return MINT_LEAVE_S; + else if (opcode == MINT_LEAVE_CHECK) + return MINT_LEAVE_S_CHECK; + else if (opcode == MINT_CALL_HANDLER) + return MINT_CALL_HANDLER_S; + else + return opcode; + } + + if (opcode >= MINT_BRFALSE_I4 && opcode <= MINT_BRTRUE_R8) + return opcode + MINT_BRFALSE_I4_S - MINT_BRFALSE_I4; + + if (opcode >= MINT_BEQ_I4 && opcode <= MINT_BLT_UN_R8) + return opcode + MINT_BEQ_I4_S - MINT_BEQ_I4; + + // Already short branch + return opcode; +} + static guint16* emit_compacted_instruction (TransformData *td, guint16* start_ip, InterpInst *ins) @@ -7681,56 +7720,47 @@ emit_compacted_instruction (TransformData *td, guint16* start_ip, InterpInst *in *ip++ = 0xdead; *ip++ = 0xbeef; } - } else if ((opcode >= MINT_BRFALSE_I4_S && opcode <= MINT_BRTRUE_R8_S) || - (opcode >= MINT_BEQ_I4_S && opcode <= MINT_BLT_UN_R8_S) || - (opcode >= MINT_BRFALSE_I4_SP && opcode <= MINT_BLT_UN_I8_IMM_SP) || - opcode == MINT_BR_S || opcode == MINT_LEAVE_S || opcode == MINT_LEAVE_S_CHECK || opcode == MINT_CALL_HANDLER_S) { + } else if (MINT_IS_UNCONDITIONAL_BRANCH (opcode) || MINT_IS_CONDITIONAL_BRANCH (opcode) || MINT_IS_SUPER_BRANCH (opcode)) { const int br_offset = start_ip - td->new_code; gboolean has_imm = opcode >= MINT_BEQ_I4_IMM_SP && opcode <= MINT_BLT_UN_I8_IMM_SP; for (int i = 0; i < mono_interp_op_sregs [opcode]; i++) *ip++ = td->locals [ins->sregs [i]].offset; if (has_imm) *ip++ = ins->data [0]; + if (ins->info.target_bb->native_offset >= 0) { + int offset = ins->info.target_bb->native_offset - br_offset; // Backwards branch. We can already patch it. - *ip++ = ins->info.target_bb->native_offset - br_offset; - } else if (opcode == MINT_BR_S && ins->info.target_bb == td->cbb->next_bb) { - // Ignore branch to the next basic block. Revert the added MINT_BR_S. + if (is_short_offset (br_offset, ins->info.target_bb->native_offset)) { + // Replace the long opcode we added at the start + *start_ip = get_short_brop (opcode); + *ip++ = ins->info.target_bb->native_offset - br_offset; + } else { + WRITE32 (ip, &offset); + } + } else if (opcode == MINT_BR && ins->info.target_bb == td->cbb->next_bb) { + // Ignore branch to the next basic block. Revert the added MINT_BR. ip--; } else { + // If the estimate offset is short, then surely the real offset is short + gboolean is_short = is_short_offset (br_offset, ins->info.target_bb->native_offset_estimate); + if (is_short) + *start_ip = get_short_brop (opcode); + // We don't know the in_offset of the target, add a reloc Reloc *reloc = (Reloc*)mono_mempool_alloc0 (td->mempool, sizeof (Reloc)); - reloc->type = RELOC_SHORT_BRANCH; + reloc->type = is_short ? RELOC_SHORT_BRANCH : RELOC_LONG_BRANCH; reloc->skip = mono_interp_op_sregs [opcode] + has_imm; reloc->offset = br_offset; reloc->target_bb = ins->info.target_bb; g_ptr_array_add (td->relocs, reloc); *ip++ = 0xdead; - } - if (opcode == MINT_CALL_HANDLER_S) - *ip++ = ins->data [1]; - } else if ((opcode >= MINT_BRFALSE_I4 && opcode <= MINT_BRTRUE_R8) || - (opcode >= MINT_BEQ_I4 && opcode <= MINT_BLT_UN_R8) || - opcode == MINT_BR || opcode == MINT_LEAVE || opcode == MINT_LEAVE_CHECK || opcode == MINT_CALL_HANDLER) { - const int br_offset = start_ip - td->new_code; - for (int i = 0; i < mono_interp_op_sregs [opcode]; i++) - *ip++ = td->locals [ins->sregs [i]].offset; - if (ins->info.target_bb->native_offset >= 0) { - // Backwards branch. We can already patch it - int target_offset = ins->info.target_bb->native_offset - br_offset; - WRITE32 (ip, &target_offset); - } else { - Reloc *reloc = (Reloc*)mono_mempool_alloc0 (td->mempool, sizeof (Reloc)); - reloc->type = RELOC_LONG_BRANCH; - reloc->skip = mono_interp_op_sregs [opcode]; - reloc->offset = br_offset; - reloc->target_bb = ins->info.target_bb; - g_ptr_array_add (td->relocs, reloc); - *ip++ = 0xdead; - *ip++ = 0xbeef; + if (!is_short) + *ip++ = 0xbeef; } if (opcode == MINT_CALL_HANDLER) *ip++ = ins->data [2]; + } else if (opcode == MINT_SDB_SEQ_POINT || opcode == MINT_IL_SEQ_POINT) { SeqPoint *seqp = (SeqPoint*)mono_mempool_alloc0 (td->mempool, sizeof (SeqPoint)); InterpBasicBlock *cbb; @@ -7800,7 +7830,7 @@ emit_compacted_instruction (TransformData *td, guint16* start_ip, InterpInst *in #endif } else if (opcode >= MINT_MOV_8_2 && opcode <= MINT_MOV_8_4) { // This instruction is not marked as operating on any vars, all instruction slots are - // actually vas. Resolve their offset + // actually vars. Resolve their offset int num_vars = mono_interp_oplen [opcode] - 1; for (int i = 0; i < num_vars; i++) *ip++ = td->locals [ins->data [i]].offset; @@ -7834,18 +7864,13 @@ static void generate_compacted_code (TransformData *td) { guint16 *ip; - int size = 0; + int size; td->relocs = g_ptr_array_new (); InterpBasicBlock *bb; - // Iterate once for preliminary computations - for (bb = td->entry_bb; bb != NULL; bb = bb->next_bb) { - InterpInst *ins = bb->first_ins; - while (ins) { - size += get_inst_length (ins); - ins = ins->next; - } - } + // This iteration could be avoided at the cost of less precise size result, following + // super instruction pass + size = compute_native_offset_estimates (td); // Generate the compacted stream of instructions td->new_code = ip = (guint16*)mono_mem_manager_alloc0 (td->mem_manager, size * sizeof (guint16)); @@ -8072,7 +8097,7 @@ interp_fold_unop (TransformData *td, LocalValue *local_defs, InterpInst *ins) #define INTERP_FOLD_UNOP_BR(_opcode,_local_type,_cond) \ case _opcode: \ if (_cond) { \ - ins->opcode = MINT_BR_S; \ + ins->opcode = MINT_BR; \ if (cbb->next_bb != ins->info.target_bb) \ interp_unlink_bblocks (cbb, cbb->next_bb); \ for (InterpInst *it = ins->next; it != NULL; it = it->next) \ @@ -8096,10 +8121,10 @@ interp_fold_unop_cond_br (TransformData *td, InterpBasicBlock *cbb, LocalValue * // Top of the stack is a constant switch (ins->opcode) { - INTERP_FOLD_UNOP_BR (MINT_BRFALSE_I4_S, LOCAL_VALUE_I4, val->i == 0); - INTERP_FOLD_UNOP_BR (MINT_BRFALSE_I8_S, LOCAL_VALUE_I8, val->l == 0); - INTERP_FOLD_UNOP_BR (MINT_BRTRUE_I4_S, LOCAL_VALUE_I4, val->i != 0); - INTERP_FOLD_UNOP_BR (MINT_BRTRUE_I8_S, LOCAL_VALUE_I8, val->l != 0); + INTERP_FOLD_UNOP_BR (MINT_BRFALSE_I4, LOCAL_VALUE_I4, val->i == 0); + INTERP_FOLD_UNOP_BR (MINT_BRFALSE_I8, LOCAL_VALUE_I8, val->l == 0); + INTERP_FOLD_UNOP_BR (MINT_BRTRUE_I4, LOCAL_VALUE_I4, val->i != 0); + INTERP_FOLD_UNOP_BR (MINT_BRTRUE_I8, LOCAL_VALUE_I8, val->l != 0); default: return ins; @@ -8248,11 +8273,11 @@ interp_fold_binop (TransformData *td, LocalValue *local_defs, InterpInst *ins, g // Due to poor current design, the branch op might not be the last instruction in the bblock // (in case we fallthrough and need to have the stack locals match the ones from next_bb, done // in fixup_newbb_stack_locals). If that's the case, clear all these mov's. This helps bblock -// merging quickly find the MINT_BR_S opcode. +// merging quickly find the MINT_BR opcode. #define INTERP_FOLD_BINOP_BR(_opcode,_local_type,_cond) \ case _opcode: \ if (_cond) { \ - ins->opcode = MINT_BR_S; \ + ins->opcode = MINT_BR; \ if (cbb->next_bb != ins->info.target_bb) \ interp_unlink_bblocks (cbb, cbb->next_bb); \ for (InterpInst *it = ins->next; it != NULL; it = it->next) \ @@ -8279,27 +8304,27 @@ interp_fold_binop_cond_br (TransformData *td, InterpBasicBlock *cbb, LocalValue return ins; switch (ins->opcode) { - INTERP_FOLD_BINOP_BR (MINT_BEQ_I4_S, LOCAL_VALUE_I4, val1->i == val2->i); - INTERP_FOLD_BINOP_BR (MINT_BEQ_I8_S, LOCAL_VALUE_I8, val1->l == val2->l); - INTERP_FOLD_BINOP_BR (MINT_BGE_I4_S, LOCAL_VALUE_I4, val1->i >= val2->i); - INTERP_FOLD_BINOP_BR (MINT_BGE_I8_S, LOCAL_VALUE_I8, val1->l >= val2->l); - INTERP_FOLD_BINOP_BR (MINT_BGT_I4_S, LOCAL_VALUE_I4, val1->i > val2->i); - INTERP_FOLD_BINOP_BR (MINT_BGT_I8_S, LOCAL_VALUE_I8, val1->l > val2->l); - INTERP_FOLD_BINOP_BR (MINT_BLT_I4_S, LOCAL_VALUE_I4, val1->i < val2->i); - INTERP_FOLD_BINOP_BR (MINT_BLT_I8_S, LOCAL_VALUE_I8, val1->l < val2->l); - INTERP_FOLD_BINOP_BR (MINT_BLE_I4_S, LOCAL_VALUE_I4, val1->i <= val2->i); - INTERP_FOLD_BINOP_BR (MINT_BLE_I8_S, LOCAL_VALUE_I8, val1->l <= val2->l); - - INTERP_FOLD_BINOP_BR (MINT_BNE_UN_I4_S, LOCAL_VALUE_I4, val1->i != val2->i); - INTERP_FOLD_BINOP_BR (MINT_BNE_UN_I8_S, LOCAL_VALUE_I8, val1->l != val2->l); - INTERP_FOLD_BINOP_BR (MINT_BGE_UN_I4_S, LOCAL_VALUE_I4, (guint32)val1->i >= (guint32)val2->i); - INTERP_FOLD_BINOP_BR (MINT_BGE_UN_I8_S, LOCAL_VALUE_I8, (guint64)val1->l >= (guint64)val2->l); - INTERP_FOLD_BINOP_BR (MINT_BGT_UN_I4_S, LOCAL_VALUE_I4, (guint32)val1->i > (guint32)val2->i); - INTERP_FOLD_BINOP_BR (MINT_BGT_UN_I8_S, LOCAL_VALUE_I8, (guint64)val1->l > (guint64)val2->l); - INTERP_FOLD_BINOP_BR (MINT_BLE_UN_I4_S, LOCAL_VALUE_I4, (guint32)val1->i <= (guint32)val2->i); - INTERP_FOLD_BINOP_BR (MINT_BLE_UN_I8_S, LOCAL_VALUE_I8, (guint64)val1->l <= (guint64)val2->l); - INTERP_FOLD_BINOP_BR (MINT_BLT_UN_I4_S, LOCAL_VALUE_I4, (guint32)val1->i < (guint32)val2->i); - INTERP_FOLD_BINOP_BR (MINT_BLT_UN_I8_S, LOCAL_VALUE_I8, (guint64)val1->l < (guint64)val2->l); + INTERP_FOLD_BINOP_BR (MINT_BEQ_I4, LOCAL_VALUE_I4, val1->i == val2->i); + INTERP_FOLD_BINOP_BR (MINT_BEQ_I8, LOCAL_VALUE_I8, val1->l == val2->l); + INTERP_FOLD_BINOP_BR (MINT_BGE_I4, LOCAL_VALUE_I4, val1->i >= val2->i); + INTERP_FOLD_BINOP_BR (MINT_BGE_I8, LOCAL_VALUE_I8, val1->l >= val2->l); + INTERP_FOLD_BINOP_BR (MINT_BGT_I4, LOCAL_VALUE_I4, val1->i > val2->i); + INTERP_FOLD_BINOP_BR (MINT_BGT_I8, LOCAL_VALUE_I8, val1->l > val2->l); + INTERP_FOLD_BINOP_BR (MINT_BLT_I4, LOCAL_VALUE_I4, val1->i < val2->i); + INTERP_FOLD_BINOP_BR (MINT_BLT_I8, LOCAL_VALUE_I8, val1->l < val2->l); + INTERP_FOLD_BINOP_BR (MINT_BLE_I4, LOCAL_VALUE_I4, val1->i <= val2->i); + INTERP_FOLD_BINOP_BR (MINT_BLE_I8, LOCAL_VALUE_I8, val1->l <= val2->l); + + INTERP_FOLD_BINOP_BR (MINT_BNE_UN_I4, LOCAL_VALUE_I4, val1->i != val2->i); + INTERP_FOLD_BINOP_BR (MINT_BNE_UN_I8, LOCAL_VALUE_I8, val1->l != val2->l); + INTERP_FOLD_BINOP_BR (MINT_BGE_UN_I4, LOCAL_VALUE_I4, (guint32)val1->i >= (guint32)val2->i); + INTERP_FOLD_BINOP_BR (MINT_BGE_UN_I8, LOCAL_VALUE_I8, (guint64)val1->l >= (guint64)val2->l); + INTERP_FOLD_BINOP_BR (MINT_BGT_UN_I4, LOCAL_VALUE_I4, (guint32)val1->i > (guint32)val2->i); + INTERP_FOLD_BINOP_BR (MINT_BGT_UN_I8, LOCAL_VALUE_I8, (guint64)val1->l > (guint64)val2->l); + INTERP_FOLD_BINOP_BR (MINT_BLE_UN_I4, LOCAL_VALUE_I4, (guint32)val1->i <= (guint32)val2->i); + INTERP_FOLD_BINOP_BR (MINT_BLE_UN_I8, LOCAL_VALUE_I8, (guint64)val1->l <= (guint64)val2->l); + INTERP_FOLD_BINOP_BR (MINT_BLT_UN_I4, LOCAL_VALUE_I4, (guint32)val1->i < (guint32)val2->i); + INTERP_FOLD_BINOP_BR (MINT_BLT_UN_I8, LOCAL_VALUE_I8, (guint64)val1->l < (guint64)val2->l); default: return ins; @@ -8709,26 +8734,26 @@ static int get_binop_condbr_imm_sp (int opcode) { switch (opcode) { - case MINT_BEQ_I4_S: return MINT_BEQ_I4_IMM_SP; - case MINT_BEQ_I8_S: return MINT_BEQ_I8_IMM_SP; - case MINT_BGE_I4_S: return MINT_BGE_I4_IMM_SP; - case MINT_BGE_I8_S: return MINT_BGE_I8_IMM_SP; - case MINT_BGT_I4_S: return MINT_BGT_I4_IMM_SP; - case MINT_BGT_I8_S: return MINT_BGT_I8_IMM_SP; - case MINT_BLT_I4_S: return MINT_BLT_I4_IMM_SP; - case MINT_BLT_I8_S: return MINT_BLT_I8_IMM_SP; - case MINT_BLE_I4_S: return MINT_BLE_I4_IMM_SP; - case MINT_BLE_I8_S: return MINT_BLE_I8_IMM_SP; - case MINT_BNE_UN_I4_S: return MINT_BNE_UN_I4_IMM_SP; - case MINT_BNE_UN_I8_S: return MINT_BNE_UN_I8_IMM_SP; - case MINT_BGE_UN_I4_S: return MINT_BGE_UN_I4_IMM_SP; - case MINT_BGE_UN_I8_S: return MINT_BGE_UN_I8_IMM_SP; - case MINT_BGT_UN_I4_S: return MINT_BGT_UN_I4_IMM_SP; - case MINT_BGT_UN_I8_S: return MINT_BGT_UN_I8_IMM_SP; - case MINT_BLE_UN_I4_S: return MINT_BLE_UN_I4_IMM_SP; - case MINT_BLE_UN_I8_S: return MINT_BLE_UN_I8_IMM_SP; - case MINT_BLT_UN_I4_S: return MINT_BLT_UN_I4_IMM_SP; - case MINT_BLT_UN_I8_S: return MINT_BLT_UN_I8_IMM_SP; + case MINT_BEQ_I4: return MINT_BEQ_I4_IMM_SP; + case MINT_BEQ_I8: return MINT_BEQ_I8_IMM_SP; + case MINT_BGE_I4: return MINT_BGE_I4_IMM_SP; + case MINT_BGE_I8: return MINT_BGE_I8_IMM_SP; + case MINT_BGT_I4: return MINT_BGT_I4_IMM_SP; + case MINT_BGT_I8: return MINT_BGT_I8_IMM_SP; + case MINT_BLT_I4: return MINT_BLT_I4_IMM_SP; + case MINT_BLT_I8: return MINT_BLT_I8_IMM_SP; + case MINT_BLE_I4: return MINT_BLE_I4_IMM_SP; + case MINT_BLE_I8: return MINT_BLE_I8_IMM_SP; + case MINT_BNE_UN_I4: return MINT_BNE_UN_I4_IMM_SP; + case MINT_BNE_UN_I8: return MINT_BNE_UN_I8_IMM_SP; + case MINT_BGE_UN_I4: return MINT_BGE_UN_I4_IMM_SP; + case MINT_BGE_UN_I8: return MINT_BGE_UN_I8_IMM_SP; + case MINT_BGT_UN_I4: return MINT_BGT_UN_I4_IMM_SP; + case MINT_BGT_UN_I8: return MINT_BGT_UN_I8_IMM_SP; + case MINT_BLE_UN_I4: return MINT_BLE_UN_I4_IMM_SP; + case MINT_BLE_UN_I8: return MINT_BLE_UN_I8_IMM_SP; + case MINT_BLT_UN_I4: return MINT_BLT_UN_I4_IMM_SP; + case MINT_BLT_UN_I8: return MINT_BLT_UN_I8_IMM_SP; default: return MINT_NOP; } } @@ -8737,26 +8762,26 @@ static int get_binop_condbr_sp (int opcode) { switch (opcode) { - case MINT_BEQ_I4_S: return MINT_BEQ_I4_SP; - case MINT_BEQ_I8_S: return MINT_BEQ_I8_SP; - case MINT_BGE_I4_S: return MINT_BGE_I4_SP; - case MINT_BGE_I8_S: return MINT_BGE_I8_SP; - case MINT_BGT_I4_S: return MINT_BGT_I4_SP; - case MINT_BGT_I8_S: return MINT_BGT_I8_SP; - case MINT_BLT_I4_S: return MINT_BLT_I4_SP; - case MINT_BLT_I8_S: return MINT_BLT_I8_SP; - case MINT_BLE_I4_S: return MINT_BLE_I4_SP; - case MINT_BLE_I8_S: return MINT_BLE_I8_SP; - case MINT_BNE_UN_I4_S: return MINT_BNE_UN_I4_SP; - case MINT_BNE_UN_I8_S: return MINT_BNE_UN_I8_SP; - case MINT_BGE_UN_I4_S: return MINT_BGE_UN_I4_SP; - case MINT_BGE_UN_I8_S: return MINT_BGE_UN_I8_SP; - case MINT_BGT_UN_I4_S: return MINT_BGT_UN_I4_SP; - case MINT_BGT_UN_I8_S: return MINT_BGT_UN_I8_SP; - case MINT_BLE_UN_I4_S: return MINT_BLE_UN_I4_SP; - case MINT_BLE_UN_I8_S: return MINT_BLE_UN_I8_SP; - case MINT_BLT_UN_I4_S: return MINT_BLT_UN_I4_SP; - case MINT_BLT_UN_I8_S: return MINT_BLT_UN_I8_SP; + case MINT_BEQ_I4: return MINT_BEQ_I4_SP; + case MINT_BEQ_I8: return MINT_BEQ_I8_SP; + case MINT_BGE_I4: return MINT_BGE_I4_SP; + case MINT_BGE_I8: return MINT_BGE_I8_SP; + case MINT_BGT_I4: return MINT_BGT_I4_SP; + case MINT_BGT_I8: return MINT_BGT_I8_SP; + case MINT_BLT_I4: return MINT_BLT_I4_SP; + case MINT_BLT_I8: return MINT_BLT_I8_SP; + case MINT_BLE_I4: return MINT_BLE_I4_SP; + case MINT_BLE_I8: return MINT_BLE_I8_SP; + case MINT_BNE_UN_I4: return MINT_BNE_UN_I4_SP; + case MINT_BNE_UN_I8: return MINT_BNE_UN_I8_SP; + case MINT_BGE_UN_I4: return MINT_BGE_UN_I4_SP; + case MINT_BGE_UN_I8: return MINT_BGE_UN_I8_SP; + case MINT_BGT_UN_I4: return MINT_BGT_UN_I4_SP; + case MINT_BGT_UN_I8: return MINT_BGT_UN_I8_SP; + case MINT_BLE_UN_I4: return MINT_BLE_UN_I4_SP; + case MINT_BLE_UN_I8: return MINT_BLE_UN_I8_SP; + case MINT_BLT_UN_I4: return MINT_BLT_UN_I4_SP; + case MINT_BLT_UN_I8: return MINT_BLT_UN_I8_SP; default: return MINT_NOP; } } @@ -8765,10 +8790,10 @@ static int get_unop_condbr_sp (int opcode) { switch (opcode) { - case MINT_BRFALSE_I4_S: return MINT_BRFALSE_I4_SP; - case MINT_BRFALSE_I8_S: return MINT_BRFALSE_I8_SP; - case MINT_BRTRUE_I4_S: return MINT_BRTRUE_I4_SP; - case MINT_BRTRUE_I8_S: return MINT_BRTRUE_I8_SP; + case MINT_BRFALSE_I4: return MINT_BRFALSE_I4_SP; + case MINT_BRFALSE_I8: return MINT_BRFALSE_I8_SP; + case MINT_BRTRUE_I4: return MINT_BRTRUE_I4_SP; + case MINT_BRTRUE_I8: return MINT_BRTRUE_I8_SP; default: return MINT_NOP; } } @@ -8778,16 +8803,20 @@ interp_super_instructions (TransformData *td) { InterpBasicBlock *bb; int *local_ref_count = td->local_ref_count; + + compute_native_offset_estimates (td); + // Add some actual super instructions for (bb = td->entry_bb; bb != NULL; bb = bb->next_bb) { InterpInst *ins; + int noe; // Set cbb since we do some instruction inserting below td->cbb = bb; - + noe = bb->native_offset_estimate; for (ins = bb->first_ins; ins != NULL; ins = ins->next) { int opcode = ins->opcode; - if (opcode == MINT_NOP) + if (MINT_IS_NOP (opcode)) continue; if (mono_interp_op_dregs [opcode] && !(td->locals [ins->dreg].flags & INTERP_LOCAL_FLAG_GLOBAL)) td->locals [ins->dreg].def = ins; @@ -8955,7 +8984,7 @@ interp_super_instructions (TransformData *td) local_ref_count [obj_sreg]--; mono_interp_stats.super_instructions++; } - } else if (MINT_IS_BINOP_CONDITIONAL_BRANCH (opcode)) { + } else if (MINT_IS_BINOP_CONDITIONAL_BRANCH (opcode) && is_short_offset (noe, ins->info.target_bb->native_offset_estimate)) { gint16 imm; int sreg_imm = ins->sregs [1]; if (get_sreg_imm (td, sreg_imm, &imm)) { @@ -8991,7 +9020,7 @@ interp_super_instructions (TransformData *td) } } } - } else if (MINT_IS_UNOP_CONDITIONAL_BRANCH (opcode)) { + } else if (MINT_IS_UNOP_CONDITIONAL_BRANCH (opcode) && is_short_offset (noe, ins->info.target_bb->native_offset_estimate)) { InterpInst *prev_ins = interp_prev_ins (ins); if (prev_ins && prev_ins->opcode == MINT_SAFEPOINT) { int condbr_op = get_unop_condbr_sp (opcode); @@ -9006,6 +9035,7 @@ interp_super_instructions (TransformData *td) } } + noe += get_inst_length (ins); } } } diff --git a/src/mono/mono/mini/interp/transform.h b/src/mono/mono/mini/interp/transform.h index c57b0981d1b1d..80a2eae7486a4 100644 --- a/src/mono/mono/mini/interp/transform.h +++ b/src/mono/mono/mini/interp/transform.h @@ -99,7 +99,16 @@ struct _InterpBasicBlock { gint16 out_count; InterpBasicBlock **out_bb; + /* The real native offset of this bblock, computed when emitting the instructions in the code stream */ int native_offset; + /* + * Estimated native offset computed before the final code stream is generated. These offsets are used + * to determine whether we will use a long or short branch when branching to this bblock. Native offset + * estimates must respect the following condition: |bb1->n_o_e - bb2->n_o_e| >= |bb1->n_o - bb2->n_o|. + * The real native offset between two instructions is always smaller or equal to the estimate, allowing + * us to safely insert short branches based on the estimated offset. + */ + int native_offset_estimate; /* * The state of the stack when entering this basic block. By default, the stack height is From d6c3ad13826da18557ae20b1b08d56e728b601a4 Mon Sep 17 00:00:00 2001 From: Thays Grazia Date: Tue, 12 Oct 2021 11:13:17 -0300 Subject: [PATCH 044/106] [wasm] Fixing compilation on windows using debug mode. (#60260) * Fixing compilation on windows using debug mode. * Fixing as suggested by @radekdoulik * fix release compilation --- src/mono/wasm/wasm.proj | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/mono/wasm/wasm.proj b/src/mono/wasm/wasm.proj index 1dcbf4bf9f206..b21ce74b3869d 100644 --- a/src/mono/wasm/wasm.proj +++ b/src/mono/wasm/wasm.proj @@ -150,9 +150,8 @@ $(ArtifactsObjDir)wasm/pinvoke-table.h -g -Os -s -DDEBUG=1 -Oz - -DEMSDK_PATH="$(EMSDK_PATH.TrimEnd('\/'))" - emcmake cmake $(MSBuildThisFileDirectory)runtime -DCONFIGURATION_EMCC_FLAGS="$(CMakeConfigurationEmccFlags)" -DMONO_INCLUDES="$(MonoArtifactsPath)include/mono-2.0" -DMONO_OBJ_INCLUDES="$(MonoObjDir.TrimEnd('\/'))" -DICU_LIB_DIR="$(ICULibDir.TrimEnd('\/'))" -DMONO_ARTIFACTS_DIR="$(MonoArtifactsPath.TrimEnd('\/'))" -DNATIVE_BIN_DIR="$(NativeBinDir.TrimEnd('\/'))" -DSYSTEM_NATIVE_DIR="$(RepoRoot)src/libraries/Native/Unix/System.Native" -DSOURCE_DIR="$(MSBuildThisFileDirectory.TrimEnd('\/'))/runtime"$(CMakeConfigurationEmsdkPath) - emcmake cmake $(MSBuildThisFileDirectory)runtime -DCONFIGURATION_EMCC_FLAGS="$(CMakeConfigurationEmccFlags)" -DMONO_INCLUDES="$(MonoArtifactsPath)include/mono-2.0" -DMONO_OBJ_INCLUDES="$(MonoObjDir.TrimEnd('\/'))" -DICU_LIB_DIR="$(ICULibDir.TrimEnd('\/'))" -DMONO_ARTIFACTS_DIR="$(MonoArtifactsPath.TrimEnd('\/'))" -DNATIVE_BIN_DIR="$(NativeBinDir.TrimEnd('\/'))" -DSYSTEM_NATIVE_DIR="$(RepoRoot)src/libraries/Native/Unix/System.Native" -DEMSDK_PATH="$(EMSDK_PATH.TrimEnd('\/'))" -DSOURCE_DIR="$(MSBuildThisFileDirectory.TrimEnd('\/'))/runtime" + -DEMSDK_PATH="$(EMSDK_PATH.TrimEnd('\/'))" + emcmake cmake $(MSBuildThisFileDirectory)runtime -DCMAKE_BUILD_TYPE=$(Configuration) -DCONFIGURATION_EMCC_FLAGS="$(CMakeConfigurationEmccFlags)" -DMONO_INCLUDES="$(MonoArtifactsPath)include/mono-2.0" -DMONO_OBJ_INCLUDES="$(MonoObjDir.TrimEnd('\/'))" -DICU_LIB_DIR="$(ICULibDir.TrimEnd('\/'))" -DMONO_ARTIFACTS_DIR="$(MonoArtifactsPath.TrimEnd('\/'))" -DNATIVE_BIN_DIR="$(NativeBinDir.TrimEnd('\/'))" -DSYSTEM_NATIVE_DIR="$(RepoRoot)src/libraries/Native/Unix/System.Native" -DSOURCE_DIR="$(MSBuildThisFileDirectory.TrimEnd('\/'))/runtime"$(CMakeConfigurationEmsdkPath) call "$(RepositoryEngineeringDir)native\init-vs-env.cmd" && call "$([MSBuild]::NormalizePath('$(EMSDK_PATH)', 'emsdk_env.bat'))" && $(CMakeBuildRuntimeConfigureCmd) bash -c 'source $(EMSDK_PATH)/emsdk_env.sh 2>&1 && $(CMakeBuildRuntimeConfigureCmd)' From 31c38ef019883f3042119b5abfcd84e3c639ad99 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 12 Oct 2021 17:28:55 +0200 Subject: [PATCH 045/106] Update dependencies from https://github.com/dotnet/linker build 20211011.1 (#60289) Microsoft.NET.ILLink.Tasks From Version 7.0.100-1.21504.4 -> To Version 7.0.100-1.21511.1 Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 4 ++-- eng/Versions.props | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 2fe8e174f10e8..5cb354e673221 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -198,9 +198,9 @@ https://github.com/dotnet/runtime 03e90a540cb2dfe5cab4086ea54ad5dd1f655749 - + https://github.com/dotnet/linker - b0b20476551dacaf86946aeb957973a8540fb960 + 48d67e3d34016141825d99b2fcc1acdad65ce3bb https://github.com/dotnet/xharness diff --git a/eng/Versions.props b/eng/Versions.props index 9824bb7e0d0f2..34d70f76e22c5 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -172,7 +172,7 @@ 6.0.0-preview-20210916.1 - 7.0.100-1.21504.4 + 7.0.100-1.21511.1 $(MicrosoftNETILLinkTasksVersion) 7.0.0-alpha.1.21477.1 From 784687f1af6a579c6e99dd31b0664fab17bbc4ea Mon Sep 17 00:00:00 2001 From: Layomi Akinrinade Date: Tue, 12 Oct 2021 12:02:36 -0400 Subject: [PATCH 046/106] Prevent runtime prop metadata retrieval when [JsonIgnore] is used (#60024) * Prevent runtime prop metadata retrieval when [JsonIgnore] is used * Address feedback * Re-add ctor param default handling tests * Remove new concurrent dictionary for generic method holders --- .../Common/ReflectionExtensions.cs | 13 +++ .../gen/JsonSourceGenerator.Emitter.cs | 13 ++- .../gen/JsonSourceGenerator.Parser.cs | 23 +++- ...ParameterizedConstructorConverter.Large.cs | 5 +- .../Serialization/JsonSerializerOptions.cs | 18 ++- .../Metadata/GenericMethodHolder.cs | 17 +++ .../Metadata/JsonParameterInfo.cs | 47 ++++++-- .../Metadata/JsonParameterInfoOfT.cs | 4 +- .../Metadata/JsonPropertyInfo.cs | 9 +- .../Metadata/JsonPropertyInfoOfT.cs | 3 + .../Metadata/JsonTypeInfo.Cache.cs | 14 +-- .../Serialization/Metadata/JsonTypeInfo.cs | 16 +-- .../ConstructorTests.ParameterMatching.cs | 106 ++++++++++++++++++ .../tests/Common/PropertyVisibilityTests.cs | 75 +++++++++++++ .../Serialization/ConstructorTests.cs | 10 ++ .../Serialization/PropertyVisibilityTests.cs | 8 ++ 16 files changed, 335 insertions(+), 46 deletions(-) diff --git a/src/libraries/System.Text.Json/Common/ReflectionExtensions.cs b/src/libraries/System.Text.Json/Common/ReflectionExtensions.cs index 8a1a58a97a2cb..09ecc45077fe0 100644 --- a/src/libraries/System.Text.Json/Common/ReflectionExtensions.cs +++ b/src/libraries/System.Text.Json/Common/ReflectionExtensions.cs @@ -310,5 +310,18 @@ public static bool TryGetDeserializationConstructor( deserializationCtor = ctorWithAttribute ?? publicParameterlessCtor ?? lonePublicCtor; return true; } + + public static object? GetDefaultValue(this ParameterInfo parameterInfo) + { + object? defaultValue = parameterInfo.DefaultValue; + + // DBNull.Value is sometimes used as the default value (returned by reflection) of nullable params in place of null. + if (defaultValue == DBNull.Value && parameterInfo.ParameterType != typeof(DBNull)) + { + return null; + } + + return defaultValue; + } } } diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs index c17eb09a805e7..ec6124a8222be 100644 --- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs +++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs @@ -804,14 +804,19 @@ private string GenerateCtorParamMetadataInitFunc(TypeGenerationSpec typeGenerati { ParameterInfo reflectionInfo = parameters[i].ParameterInfo; + string parameterTypeRef = reflectionInfo.ParameterType.GetCompilableName(); + + object? defaultValue = reflectionInfo.GetDefaultValue(); + string defaultValueAsStr = GetParamDefaultValueAsString(defaultValue, parameterTypeRef); + sb.Append(@$" {InfoVarName} = new() {{ Name = ""{reflectionInfo.Name!}"", - ParameterType = typeof({reflectionInfo.ParameterType.GetCompilableName()}), + ParameterType = typeof({parameterTypeRef}), Position = {reflectionInfo.Position}, HasDefaultValue = {ToCSharpKeyword(reflectionInfo.HasDefaultValue)}, - DefaultValue = {GetParamDefaultValueAsString(reflectionInfo.DefaultValue)} + DefaultValue = {defaultValueAsStr} }}; {parametersVarName}[{i}] = {InfoVarName}; "); @@ -1313,12 +1318,12 @@ private static string GetNumberHandlingAsStr(JsonNumberHandling? numberHandling) private static string ToCSharpKeyword(bool value) => value.ToString().ToLowerInvariant(); - private static string GetParamDefaultValueAsString(object? value) + private static string GetParamDefaultValueAsString(object? value, string objectTypeAsStr) { switch (value) { case null: - return "null"; + return $"default({objectTypeAsStr})"; case bool boolVal: return ToCSharpKeyword(boolVal); default: diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs index bbdd17bddb8c2..2785e66c7cd3a 100644 --- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs +++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs @@ -26,6 +26,7 @@ private sealed class Parser private const string SystemTextJsonNamespace = "System.Text.Json"; private const string JsonConverterAttributeFullName = "System.Text.Json.Serialization.JsonConverterAttribute"; private const string JsonConverterFactoryFullName = "System.Text.Json.Serialization.JsonConverterFactory"; + private const string JsonConverterOfTFullName = "System.Text.Json.Serialization.JsonConverter`1"; private const string JsonArrayFullName = "System.Text.Json.Nodes.JsonArray"; private const string JsonElementFullName = "System.Text.Json.JsonElement"; private const string JsonExtensionDataAttributeFullName = "System.Text.Json.Serialization.JsonExtensionDataAttribute"; @@ -101,6 +102,9 @@ private sealed class Parser private readonly Type? _dateOnlyType; private readonly Type? _timeOnlyType; + // Needed for converter validation + private readonly Type _jsonConverterOfTType; + private readonly HashSet _numberTypes = new(); private readonly HashSet _knownTypes = new(); private readonly HashSet _knownUnsupportedTypes = new(); @@ -215,6 +219,8 @@ public Parser(Compilation compilation, in JsonSourceGenerationContext sourceGene _dateOnlyType = _metadataLoadContext.Resolve(DateOnlyFullName); _timeOnlyType = _metadataLoadContext.Resolve(TimeOnlyFullName); + _jsonConverterOfTType = _metadataLoadContext.Resolve(JsonConverterOfTFullName); + PopulateKnownTypes(); } @@ -224,8 +230,12 @@ public Parser(Compilation compilation, in JsonSourceGenerationContext sourceGene INamedTypeSymbol jsonSerializerContextSymbol = compilation.GetBestTypeByMetadataName(JsonSerializerContextFullName); INamedTypeSymbol jsonSerializableAttributeSymbol = compilation.GetBestTypeByMetadataName(JsonSerializerAttributeFullName); INamedTypeSymbol jsonSourceGenerationOptionsAttributeSymbol = compilation.GetBestTypeByMetadataName(JsonSourceGenerationOptionsAttributeFullName); + INamedTypeSymbol jsonConverterOfTAttributeSymbol = compilation.GetBestTypeByMetadataName(JsonConverterOfTFullName); - if (jsonSerializerContextSymbol == null || jsonSerializableAttributeSymbol == null || jsonSourceGenerationOptionsAttributeSymbol == null) + if (jsonSerializerContextSymbol == null || + jsonSerializableAttributeSymbol == null || + jsonSourceGenerationOptionsAttributeSymbol == null || + jsonConverterOfTAttributeSymbol == null) { return null; } @@ -1354,7 +1364,14 @@ private static bool PropertyAccessorCanBeReferenced(MethodInfo? accessor) return null; } - if (converterType.GetCompatibleBaseClass(JsonConverterFactoryFullName) != null) + // Validated when creating the source generation spec. + Debug.Assert(_jsonConverterOfTType != null); + + if (converterType.GetCompatibleGenericBaseClass(_jsonConverterOfTType) != null) + { + return $"new {converterType.GetCompilableName()}()"; + } + else if (converterType.GetCompatibleBaseClass(JsonConverterFactoryFullName) != null) { hasFactoryConverter = true; @@ -1368,7 +1385,7 @@ private static bool PropertyAccessorCanBeReferenced(MethodInfo? accessor) } } - return $"new {converterType.GetCompilableName()}()"; + return null; } private static string DetermineRuntimePropName(string clrPropName, string? jsonPropName, JsonKnownNamingPolicy namingPolicy) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.Large.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.Large.cs index 3e25b10c13a23..f820ffbfe3a85 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.Large.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.Large.cs @@ -67,10 +67,7 @@ protected sealed override void InitializeConstructorArgumentCaches(ref ReadStack { JsonParameterInfo? parameterInfo = cache[i].Value; Debug.Assert(parameterInfo != null); - - arguments[parameterInfo.ClrInfo.Position] = parameterInfo.ShouldDeserialize - ? parameterInfo.DefaultValue - : parameterInfo.ClrDefaultValue; + arguments[parameterInfo.ClrInfo.Position] = parameterInfo.DefaultValue; } state.Current.CtorArgumentState!.Arguments = arguments; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs index 2d23e7c5c9200..f81025bbc460e 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs @@ -600,9 +600,7 @@ internal JsonTypeInfo GetOrAddClass(Type type) { _haveTypesBeenCreated = true; - // todo: for performance and reduced instances, consider using the converters and JsonTypeInfo from s_defaultOptions by cloning (or reference directly if no changes). - // https://github.com/dotnet/runtime/issues/32357 - if (!_classes.TryGetValue(type, out JsonTypeInfo? result)) + if (!TryGetClass(type, out JsonTypeInfo? result)) { result = _classes.GetOrAdd(type, GetClassFromContextOrCreate(type)); } @@ -644,6 +642,20 @@ internal JsonTypeInfo GetOrAddClassForRootType(Type type) return jsonTypeInfo; } + internal bool TryGetClass(Type type, [NotNullWhen(true)] out JsonTypeInfo? jsonTypeInfo) + { + // todo: for performance and reduced instances, consider using the converters and JsonTypeInfo from s_defaultOptions by cloning (or reference directly if no changes). + // https://github.com/dotnet/runtime/issues/32357 + if (!_classes.TryGetValue(type, out JsonTypeInfo? result)) + { + jsonTypeInfo = null; + return false; + } + + jsonTypeInfo = result; + return true; + } + internal bool TypeIsCached(Type type) { return _classes.ContainsKey(type); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/GenericMethodHolder.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/GenericMethodHolder.cs index a144c11d398c9..2c143f95e59cc 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/GenericMethodHolder.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/GenericMethodHolder.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Text.Json.Reflection; @@ -12,10 +13,24 @@ namespace System.Text.Json.Serialization.Metadata /// internal abstract class GenericMethodHolder { + /// + /// Returns the default value for the specified type. + /// + public abstract object? DefaultValue { get; } + /// /// Returns true if contains only default values. /// public abstract bool IsDefaultValue(object value); + + /// + /// Creates a holder instance representing a type. + /// + public static GenericMethodHolder CreateHolder(Type type) + { + Type holderType = typeof(GenericMethodHolder<>).MakeGenericType(type); + return (GenericMethodHolder)Activator.CreateInstance(holderType)!; + } } /// @@ -23,6 +38,8 @@ internal abstract class GenericMethodHolder /// internal sealed class GenericMethodHolder : GenericMethodHolder { + public override object? DefaultValue => default(T); + public override bool IsDefaultValue(object value) { // For performance, we only want to call this method for non-nullable value types. diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonParameterInfo.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonParameterInfo.cs index 7b33f3dd97a88..3905c70402024 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonParameterInfo.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonParameterInfo.cs @@ -17,8 +17,6 @@ internal abstract class JsonParameterInfo private protected bool MatchingPropertyCanBeNull { get; private set; } - internal abstract object? ClrDefaultValue { get; } - // The default value of the parameter. This is `DefaultValue` of the `ParameterInfo`, if specified, or the CLR `default` for the `ParameterType`. public object? DefaultValue { get; private protected set; } @@ -74,18 +72,49 @@ public virtual void Initialize(JsonParameterInfoValues parameterInfo, JsonProper MatchingPropertyCanBeNull = matchingProperty.PropertyTypeCanBeNull; } - // Create a parameter that is ignored at run time. It uses the same type (typeof(sbyte)) to help - // prevent issues with unsupported types and helps ensure we don't accidently (de)serialize it. - public static JsonParameterInfo CreateIgnoredParameterPlaceholder(JsonParameterInfoValues parameterInfo, JsonPropertyInfo matchingProperty) + /// + /// Create a parameter that is ignored at run time. It uses the same type (typeof(sbyte)) to help + /// prevent issues with unsupported types and helps ensure we don't accidently (de)serialize it. + /// + public static JsonParameterInfo CreateIgnoredParameterPlaceholder( + JsonParameterInfoValues parameterInfo, + JsonPropertyInfo matchingProperty, + bool sourceGenMode) { - JsonParameterInfo jsonParameterInfo = matchingProperty.ConverterBase.CreateJsonParameterInfo(); + JsonParameterInfo jsonParameterInfo = new JsonParameterInfo(); jsonParameterInfo.ClrInfo = parameterInfo; jsonParameterInfo.RuntimePropertyType = matchingProperty.RuntimePropertyType!; jsonParameterInfo.NameAsUtf8Bytes = matchingProperty.NameAsUtf8Bytes!; - jsonParameterInfo.InitializeDefaultValue(matchingProperty); + + // TODO: https://github.com/dotnet/runtime/issues/60082. + // Default value initialization for params mapping to ignored properties doesn't + // account for the default value of optional parameters. This should be fixed. + + if (sourceGenMode) + { + // The value in the matching JsonPropertyInfo instance matches the parameter type. + jsonParameterInfo.DefaultValue = matchingProperty.DefaultValue; + } + else + { + // The value in the created JsonPropertyInfo instance (sbyte) + // doesn't match the parameter type, use reflection to get the default value. + Type parameterType = parameterInfo.ParameterType; + + GenericMethodHolder holder; + if (matchingProperty.Options.TryGetClass(parameterType, out JsonTypeInfo? typeInfo)) + { + holder = typeInfo.GenericMethods; + } + else + { + holder = GenericMethodHolder.CreateHolder(parameterInfo.ParameterType); + } + + jsonParameterInfo.DefaultValue = holder.DefaultValue; + } + return jsonParameterInfo; } - - protected abstract void InitializeDefaultValue(JsonPropertyInfo matchingProperty); } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonParameterInfoOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonParameterInfoOfT.cs index 2195426a551ed..b275f01acd095 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonParameterInfoOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonParameterInfoOfT.cs @@ -11,8 +11,6 @@ namespace System.Text.Json.Serialization.Metadata /// internal sealed class JsonParameterInfo : JsonParameterInfo { - internal override object? ClrDefaultValue => default(T); - public T TypedDefaultValue { get; private set; } = default!; public override void Initialize(JsonParameterInfoValues parameterInfo, JsonPropertyInfo matchingProperty, JsonSerializerOptions options) @@ -21,7 +19,7 @@ public override void Initialize(JsonParameterInfoValues parameterInfo, JsonPrope InitializeDefaultValue(matchingProperty); } - protected override void InitializeDefaultValue(JsonPropertyInfo matchingProperty) + private void InitializeDefaultValue(JsonPropertyInfo matchingProperty) { Debug.Assert(ClrInfo.ParameterType == matchingProperty.DeclaredPropertyType); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfo.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfo.cs index 3b8c003ac3066..5e2258ec6dde8 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfo.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfo.cs @@ -42,16 +42,14 @@ internal static JsonPropertyInfo GetPropertyPlaceholder() // Create a property that is ignored at run-time. internal static JsonPropertyInfo CreateIgnoredPropertyPlaceholder( - JsonConverter converter, MemberInfo memberInfo, Type memberType, bool isVirtual, JsonSerializerOptions options) { - JsonPropertyInfo jsonPropertyInfo = converter.CreateJsonPropertyInfo(); + JsonPropertyInfo jsonPropertyInfo = new JsonPropertyInfo(); jsonPropertyInfo.Options = options; - jsonPropertyInfo.ConverterBase = converter; jsonPropertyInfo.MemberInfo = memberInfo; jsonPropertyInfo.IsIgnored = true; jsonPropertyInfo.DeclaredPropertyType = memberType; @@ -525,5 +523,10 @@ internal JsonTypeInfo RuntimeTypeInfo internal string? ClrName { get; set; } internal bool IsVirtual { get; set; } + + /// + /// Default value used for parameterized ctor invocation. + /// + internal abstract object? DefaultValue { get; } } } 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 f60f0450701b9..e98f08dd54cdc 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 @@ -28,8 +28,11 @@ internal sealed class JsonPropertyInfo : JsonPropertyInfo private bool _propertyTypeEqualsTypeToConvert; internal Func? Get { get; set; } + internal Action? Set { get; set; } + internal override object? DefaultValue => default(T); + public JsonConverter Converter { get; internal set; } = null!; internal override void Initialize( diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.Cache.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.Cache.cs index d3f2c3c9bee6f..f696cefe8f21c 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.Cache.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.Cache.cs @@ -58,6 +58,12 @@ internal static JsonPropertyInfo AddProperty( JsonNumberHandling? parentTypeNumberHandling, JsonSerializerOptions options) { + JsonIgnoreCondition? ignoreCondition = JsonPropertyInfo.GetAttribute(memberInfo)?.Condition; + if (ignoreCondition == JsonIgnoreCondition.Always) + { + return JsonPropertyInfo.CreateIgnoredPropertyPlaceholder(memberInfo, memberType, isVirtual, options); + } + JsonConverter converter = GetConverter( memberType, parentClassType, @@ -65,12 +71,6 @@ internal static JsonPropertyInfo AddProperty( out Type runtimeType, options); - JsonIgnoreCondition? ignoreCondition = JsonPropertyInfo.GetAttribute(memberInfo)?.Condition; - if (ignoreCondition == JsonIgnoreCondition.Always) - { - return JsonPropertyInfo.CreateIgnoredPropertyPlaceholder(converter, memberInfo, memberType, isVirtual, options); - } - return CreateProperty( declaredPropertyType: memberType, runtimePropertyType: runtimeType, @@ -646,7 +646,7 @@ internal void InitializeParameterCache() return; } - InitializeConstructorParameters(array); + InitializeConstructorParameters(array, sourceGenMode: true); Debug.Assert(ParameterCache != null); } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs index 4f662c67cd65c..4bd852e447e2b 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs @@ -133,12 +133,7 @@ internal GenericMethodHolder GenericMethods { get { - if (_genericMethods == null) - { - Type runtimePropertyClass = typeof(GenericMethodHolder<>).MakeGenericType(new Type[] { Type })!; - _genericMethods = (GenericMethodHolder)Activator.CreateInstance(runtimePropertyClass)!; - } - + _genericMethods ??= GenericMethodHolder.CreateHolder(Type); return _genericMethods; } } @@ -454,7 +449,7 @@ public ParameterLookupValue(JsonPropertyInfo jsonPropertyInfo) public JsonPropertyInfo JsonPropertyInfo { get; } } - private void InitializeConstructorParameters(JsonParameterInfoValues[] jsonParameters) + private void InitializeConstructorParameters(JsonParameterInfoValues[] jsonParameters, bool sourceGenMode = false) { var parameterCache = new JsonPropertyDictionary(Options.PropertyNameCaseInsensitive, jsonParameters.Length); @@ -500,7 +495,7 @@ private void InitializeConstructorParameters(JsonParameterInfoValues[] jsonParam Debug.Assert(matchingEntry.JsonPropertyInfo != null); JsonPropertyInfo jsonPropertyInfo = matchingEntry.JsonPropertyInfo; - JsonParameterInfo jsonParameterInfo = CreateConstructorParameter(parameterInfo, jsonPropertyInfo, Options); + JsonParameterInfo jsonParameterInfo = CreateConstructorParameter(parameterInfo, jsonPropertyInfo, sourceGenMode, Options); parameterCache.Add(jsonPropertyInfo.NameAsString, jsonParameterInfo); } // It is invalid for the extension data property to bind with a constructor argument. @@ -530,7 +525,7 @@ private static JsonParameterInfoValues[] GetParameterInfoArray(ParameterInfo[] p ParameterType = reflectionInfo.ParameterType, Position = reflectionInfo.Position, HasDefaultValue = reflectionInfo.HasDefaultValue, - DefaultValue = reflectionInfo.DefaultValue + DefaultValue = reflectionInfo.GetDefaultValue() }; jsonParameters[i] = jsonInfo; @@ -580,11 +575,12 @@ private bool IsValidDataExtensionProperty(JsonPropertyInfo jsonPropertyInfo) private static JsonParameterInfo CreateConstructorParameter( JsonParameterInfoValues parameterInfo, JsonPropertyInfo jsonPropertyInfo, + bool sourceGenMode, JsonSerializerOptions options) { if (jsonPropertyInfo.IsIgnored) { - return JsonParameterInfo.CreateIgnoredParameterPlaceholder(parameterInfo, jsonPropertyInfo); + return JsonParameterInfo.CreateIgnoredParameterPlaceholder(parameterInfo, jsonPropertyInfo, sourceGenMode); } JsonConverter converter = jsonPropertyInfo.ConverterBase; diff --git a/src/libraries/System.Text.Json/tests/Common/ConstructorTests/ConstructorTests.ParameterMatching.cs b/src/libraries/System.Text.Json/tests/Common/ConstructorTests/ConstructorTests.ParameterMatching.cs index c04815dc85e6a..93aff74c1b4d0 100644 --- a/src/libraries/System.Text.Json/tests/Common/ConstructorTests/ConstructorTests.ParameterMatching.cs +++ b/src/libraries/System.Text.Json/tests/Common/ConstructorTests/ConstructorTests.ParameterMatching.cs @@ -1216,5 +1216,111 @@ public class TypeWithUri public TypeWithUri(Uri myUri = default) => MyUri = myUri; } + + [Fact] + public async Task SmallObject_ClrDefaultParamValueUsed_WhenMatchingPropIgnored() + { + string json = @"{""Prop"":20}"; + var obj1 = await JsonSerializerWrapperForString.DeserializeWrapper(json); + Assert.Equal(0, obj1.Prop); + + var obj2 = await JsonSerializerWrapperForString.DeserializeWrapper(json); + Assert.Equal(0, obj2.Prop); + } + + public class SmallType_IgnoredProp_Bind_ParamWithDefaultValue + { + [JsonIgnore] + public int Prop { get; set; } + + public SmallType_IgnoredProp_Bind_ParamWithDefaultValue(int prop = 5) + => Prop = prop; + } + + public class SmallType_IgnoredProp_Bind_Param + { + [JsonIgnore] + public int Prop { get; set; } + + public SmallType_IgnoredProp_Bind_Param(int prop) + => Prop = prop; + } + + [Fact] + public async Task LargeObject_ClrDefaultParamValueUsed_WhenMatchingPropIgnored() + { + string json = @"{""Prop"":20}"; + var obj1 = await JsonSerializerWrapperForString.DeserializeWrapper(json); + Assert.Equal(0, obj1.Prop); + + var obj2 = await JsonSerializerWrapperForString.DeserializeWrapper(json); + Assert.Equal(0, obj2.Prop); + } + + public class LargeType_IgnoredProp_Bind_ParamWithDefaultValue + { + public int W { get; set; } + + public int X { get; set; } + + public int Y { get; set; } + + public int Z { get; set; } + + [JsonIgnore] + public int Prop { get; set; } + + public LargeType_IgnoredProp_Bind_ParamWithDefaultValue(int w, int x, int y, int z, int prop = 5) + => Prop = prop; + } + + public class LargeType_IgnoredProp_Bind_Param + { + public int W { get; set; } + + public int X { get; set; } + + public int Y { get; set; } + + public int Z { get; set; } + + [JsonIgnore] + public int Prop { get; set; } + + public LargeType_IgnoredProp_Bind_Param(int w, int x, int y, int z, int prop) + => Prop = prop; + } + + [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34779")] + public async Task BindingBetweenRefProps() + { + string json = @"{""NameRef"":""John""}"; + await JsonSerializerWrapperForString.DeserializeWrapper(json); + } + + public class TypeWith_RefStringProp_ParamCtor + { + private string _name; + + public ref string NameRef => ref _name; + + public TypeWith_RefStringProp_ParamCtor(ref string nameRef) => _name = nameRef; + } + + [Fact] + public async Task BindToIgnoredPropOfSameType() + { + string json = @"{""Prop"":{}}"; + Assert.NotNull(await JsonSerializerWrapperForString.DeserializeWrapper(json)); + } + + public class ClassWithIgnoredSameType + { + [JsonIgnore] + public ClassWithIgnoredSameType Prop { get; } + + public ClassWithIgnoredSameType(ClassWithIgnoredSameType prop) { } + } } } diff --git a/src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.cs b/src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.cs index a2b5845ca44dc..a41380e3e60d9 100644 --- a/src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.cs +++ b/src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.cs @@ -2742,5 +2742,80 @@ public async Task JsonIgnoreCondition_Polymorphic() Assert.Equal(3, obj.Abstract_Property); Assert.Equal(4, obj.Virtual_Property); } + + [Fact] + public async Task SerializationMetadataNotComputedWhenMemberIgnored() + { + string janePayload = @"{""Name"":""Jane Doe""}"; + +#if !BUILDING_SOURCE_GENERATOR_TESTS + // Without [JsonIgnore], serializer throws exceptions due to runtime-reflection-based property metadata inspection. + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.SerializeWrapper(new TypeWith_RefStringProp())); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper("{}")); + + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.SerializeWrapper(new TypeWith_PropWith_BadConverter())); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper("{}")); +#else + // Ref returns supported in source-gen mode + string expected = @"{""NameRef"":""John Doe"",""Name"":""John Doe""}"; + JsonTestHelper.AssertJsonEqual(expected, await JsonSerializerWrapperForString.SerializeWrapper(new TypeWith_RefStringProp())); + + var obj = await JsonSerializerWrapperForString.DeserializeWrapper(janePayload); + Assert.Equal("Jane Doe", obj.Name); + Assert.Equal("Jane Doe", obj.NameRef); + + var obj2 = new TypeWith_PropWith_BadConverter(); + obj2.Property = "Hello"; + + // Invalid converter specified, fallback to built-in converter. This should be corrected. + // https://github.com/dotnet/runtime/issues/60020. + + Assert.Equal(@"{""Property"":""Hello""}", await JsonSerializerWrapperForString.SerializeWrapper(obj2)); + + obj2 = await JsonSerializerWrapperForString.DeserializeWrapper(@"{""Property"":""World""}"); + Assert.Equal("World", obj2.Property); +#endif + + // With [JsonIgnore], serializer skips property metadata inspection + Assert.Equal(@"{""Name"":""John Doe""}", await JsonSerializerWrapperForString.SerializeWrapper(new TypeWith_IgnoredRefStringProp())); + Assert.Equal("Jane Doe", (await JsonSerializerWrapperForString.DeserializeWrapper(janePayload)).Name); + + Assert.Equal("{}", await JsonSerializerWrapperForString.SerializeWrapper(new TypeWith_IgnoredPropWith_BadConverter())); + Assert.Null((await JsonSerializerWrapperForString.DeserializeWrapper("{}")).Property); + } + + internal class TypeWith_RefStringProp + { + public ref string NameRef => ref Name; + + [JsonInclude] // This is a field. + public string Name = "John Doe"; + } + + internal class TypeWith_IgnoredRefStringProp + { + [JsonIgnore] + public ref string NameRef => ref Name; + + [JsonInclude] // This is a field. + public string Name = "John Doe"; + } + + public class TypeWith_PropWith_BadConverter + { + [JsonConverter(typeof(BadConverter))] + public string? Property { get; set; } + } + + public class TypeWith_IgnoredPropWith_BadConverter + { + [JsonIgnore] + [JsonConverter(typeof(BadConverter))] + public string? Property { get; set; } + } + + public class BadConverter + { + } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/ConstructorTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/ConstructorTests.cs index f0d1cb897ce2d..deba91529ef37 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/ConstructorTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/ConstructorTests.cs @@ -125,6 +125,11 @@ protected ConstructorTests_Metadata(JsonSerializerWrapperForString stringWrapper [JsonSerializable(typeof(JsonElement))] [JsonSerializable(typeof(Parameterized_Class_With_ComplexTuple))] [JsonSerializable(typeof(Parameterized_Person_Simple))] + [JsonSerializable(typeof(SmallType_IgnoredProp_Bind_ParamWithDefaultValue))] + [JsonSerializable(typeof(SmallType_IgnoredProp_Bind_Param))] + [JsonSerializable(typeof(LargeType_IgnoredProp_Bind_ParamWithDefaultValue))] + [JsonSerializable(typeof(LargeType_IgnoredProp_Bind_Param))] + [JsonSerializable(typeof(ClassWithIgnoredSameType))] internal sealed partial class ConstructorTestsContext_Metadata : JsonSerializerContext { } @@ -241,6 +246,11 @@ public ConstructorTests_Default() [JsonSerializable(typeof(JsonElement))] [JsonSerializable(typeof(Parameterized_Class_With_ComplexTuple))] [JsonSerializable(typeof(Parameterized_Person_Simple))] + [JsonSerializable(typeof(SmallType_IgnoredProp_Bind_ParamWithDefaultValue))] + [JsonSerializable(typeof(SmallType_IgnoredProp_Bind_Param))] + [JsonSerializable(typeof(LargeType_IgnoredProp_Bind_ParamWithDefaultValue))] + [JsonSerializable(typeof(LargeType_IgnoredProp_Bind_Param))] + [JsonSerializable(typeof(ClassWithIgnoredSameType))] internal sealed partial class ConstructorTestsContext_Default : JsonSerializerContext { } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PropertyVisibilityTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PropertyVisibilityTests.cs index f4858804d6012..3b38cefe23e98 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PropertyVisibilityTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PropertyVisibilityTests.cs @@ -264,6 +264,10 @@ public override async Task HonorJsonPropertyName_PrivateSetter() [JsonSerializable(typeof(ClassWithValueAndReferenceTypes))] [JsonSerializable(typeof(ClassWithReadOnlyStringProperty_IgnoreWhenWritingDefault))] [JsonSerializable(typeof(ConcreteDerivedClass))] + [JsonSerializable(typeof(TypeWith_RefStringProp))] + [JsonSerializable(typeof(TypeWith_IgnoredRefStringProp))] + [JsonSerializable(typeof(TypeWith_PropWith_BadConverter))] + [JsonSerializable(typeof(TypeWith_IgnoredPropWith_BadConverter))] internal sealed partial class PropertyVisibilityTestsContext_Metadata : JsonSerializerContext { } @@ -431,6 +435,10 @@ public override async Task JsonIgnoreCondition_WhenWritingNull_OnValueType_Fail_ [JsonSerializable(typeof(ClassWithValueAndReferenceTypes))] [JsonSerializable(typeof(ClassWithReadOnlyStringProperty_IgnoreWhenWritingDefault))] [JsonSerializable(typeof(ConcreteDerivedClass))] + [JsonSerializable(typeof(TypeWith_RefStringProp))] + [JsonSerializable(typeof(TypeWith_IgnoredRefStringProp))] + [JsonSerializable(typeof(TypeWith_PropWith_BadConverter))] + [JsonSerializable(typeof(TypeWith_IgnoredPropWith_BadConverter))] internal sealed partial class PropertyVisibilityTestsContext_Default : JsonSerializerContext { } From d4e4d33e18738a78c2ce371039bb392db0a29bd8 Mon Sep 17 00:00:00 2001 From: John Call Date: Tue, 12 Oct 2021 09:51:39 -0700 Subject: [PATCH 047/106] Improve SortedSet DeepCopy performance (#56561) * SortedSet recursive deep copy * Make DeepCopy iterative * Undo accidental delete --- .../System/Collections/Generic/SortedSet.cs | 42 +++++++------------ 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/src/libraries/System.Collections/src/System/Collections/Generic/SortedSet.cs b/src/libraries/System.Collections/src/System/Collections/Generic/SortedSet.cs index 4b5b4b732fdeb..6829f94dec62e 100644 --- a/src/libraries/System.Collections/src/System/Collections/Generic/SortedSet.cs +++ b/src/libraries/System.Collections/src/System/Collections/Generic/SortedSet.cs @@ -1681,41 +1681,27 @@ public Node DeepClone(int count) #if DEBUG Debug.Assert(count == GetCount()); #endif - - // Breadth-first traversal to recreate nodes, preorder traversal to replicate nodes. - - var originalNodes = new Stack(2 * Log2(count) + 2); - var newNodes = new Stack(2 * Log2(count) + 2); Node newRoot = ShallowClone(); - Node? originalCurrent = this; - Node newCurrent = newRoot; + var pendingNodes = new Stack<(Node source, Node target)>(2 * Log2(count) + 2); + pendingNodes.Push((this, newRoot)); - while (originalCurrent != null) + while (pendingNodes.TryPop(out var next)) { - originalNodes.Push(originalCurrent); - newNodes.Push(newCurrent); - newCurrent.Left = originalCurrent.Left?.ShallowClone(); - originalCurrent = originalCurrent.Left; - newCurrent = newCurrent.Left!; - } + Node clonedNode; - while (originalNodes.Count != 0) - { - originalCurrent = originalNodes.Pop(); - newCurrent = newNodes.Pop(); - - Node? originalRight = originalCurrent.Right; - Node? newRight = originalRight?.ShallowClone(); - newCurrent.Right = newRight; + if (next.source.Left is Node left) + { + clonedNode = left.ShallowClone(); + next.target.Left = clonedNode; + pendingNodes.Push((left, clonedNode)); + } - while (originalRight != null) + if (next.source.Right is Node right) { - originalNodes.Push(originalRight); - newNodes.Push(newRight!); - newRight!.Left = originalRight.Left?.ShallowClone(); - originalRight = originalRight.Left; - newRight = newRight.Left; + clonedNode = right.ShallowClone(); + next.target.Right = clonedNode; + pendingNodes.Push((right, clonedNode)); } } From 3d8a6e6c08631977d60c07ee817a85098b9c66e0 Mon Sep 17 00:00:00 2001 From: Kunal Pathak Date: Tue, 12 Oct 2021 11:10:56 -0700 Subject: [PATCH 048/106] Some Antigen pipeline fixes (#60284) * continueOnError for subsequent tasks * Run based on RunReason * Capture logs in Antigen.log and upload it * Remove continueOnError for upload-artifacts step * Fix .proj to have PropertyGroup * Fix issue * wrap TempDir() deletion in try-except * Review comments --- .../templates/jit-run-exploratory-job.yml | 36 ++++++++++++++++++- src/coreclr/scripts/antigen_run.py | 36 ++++++++++++++----- src/coreclr/scripts/exploratory.proj | 15 ++++++-- src/coreclr/scripts/superpmi_setup.py | 21 +++++------ 4 files changed, 86 insertions(+), 22 deletions(-) diff --git a/eng/pipelines/coreclr/templates/jit-run-exploratory-job.yml b/eng/pipelines/coreclr/templates/jit-run-exploratory-job.yml index 427bba150c812..738804454d2da 100644 --- a/eng/pipelines/coreclr/templates/jit-run-exploratory-job.yml +++ b/eng/pipelines/coreclr/templates/jit-run-exploratory-job.yml @@ -54,6 +54,12 @@ jobs: - HelixApiAccessToken: '' - HelixPreCommand: '' + - ${{ if in(variables['Build.Reason'], 'Schedule') }}: + - name: RunReason + value: 'Scheduled' + - ${{ if notin(variables['Build.Reason'], 'Schedule') }}: + - name: RunReason + value: 'PR' - ${{ if eq(parameters.osGroup, 'windows') }}: - name: PythonScript value: 'py -3' @@ -65,6 +71,8 @@ jobs: value: '$(Build.SourcesDirectory)\artifacts\helixresults\' - name: IssuesSummaryLocation value: '$(Build.SourcesDirectory)\artifacts\issues_summary\' + - name: AntigenLogsLocation + value: '$(Build.SourcesDirectory)\artifacts\antigen_logs\' - ${{ if ne(parameters.osGroup, 'windows') }}: - name: PythonScript @@ -77,6 +85,8 @@ jobs: value: '$(Build.SourcesDirectory)/artifacts/helixresults/' - name: IssuesSummaryLocation value: '$(Build.SourcesDirectory)/artifacts/issues_summary/' + - name: AntigenLogsLocation + value: '$(Build.SourcesDirectory)/artifacts/antigen_logs/' workspace: clean: all pool: @@ -110,6 +120,7 @@ jobs: osGroup: ${{ parameters.osGroup }} RunConfiguration: '$(RunConfiguration)' ToolName: '$(ToolName)' + RunReason: '$(RunReason)' continueOnError: true - template: /eng/pipelines/common/upload-artifact-step.yml @@ -129,20 +140,43 @@ jobs: sourceFolder: '$(IssuesLocation)' contents: '**/issues-summary-*.txt' targetFolder: '$(IssuesSummaryLocation)' + continueOnError: true + condition: always() - task: PublishPipelineArtifact@1 displayName: Publish issues-summary.txt files inputs: targetPath: $(IssuesSummaryLocation) artifactName: 'Issues_Summary_$(osGroup)$(osSubgroup)_$(archType)_$(buildConfig)' + continueOnError: true + condition: always() + + # Always upload the available Antigen.log files + - task: CopyFiles@2 + displayName: Copying Antigen.logs of all partitions + inputs: + sourceFolder: '$(IssuesLocation)' + contents: '**/Antigen-*.log' + targetFolder: '$(AntigenLogsLocation)' + continueOnError: true + condition: always() + + - task: PublishPipelineArtifact@1 + displayName: Publish Antigen.log files + inputs: + targetPath: $(AntigenLogsLocation) + artifactName: 'Antigen_Logs_$(osGroup)$(osSubgroup)_$(archType)_$(buildConfig)' + continueOnError: true + condition: always() - script: $(PythonScript) $(Build.SourcesDirectory)/src/coreclr/scripts/antigen_unique_issues.py -issues_directory $(IssuesSummaryLocation) displayName: ${{ format('Print unique issues ({0})', parameters.osGroup) }} continueOnError: true + condition: always() - task: PublishPipelineArtifact@1 displayName: Publish Antigen build logs inputs: targetPath: $(Build.SourcesDirectory)/artifacts/log - artifactName: 'Antigen_BuildLogs__$(archType)_$(buildConfig)' + artifactName: 'Antigen_BuildLogs_$(osGroup)$(osSubgroup)_$(archType)_$(buildConfig)' condition: always() \ No newline at end of file diff --git a/src/coreclr/scripts/antigen_run.py b/src/coreclr/scripts/antigen_run.py index e97196debeedf..e8a550a15b7a2 100644 --- a/src/coreclr/scripts/antigen_run.py +++ b/src/coreclr/scripts/antigen_run.py @@ -30,6 +30,7 @@ parser.add_argument("-output_directory", help="Path to output directory") parser.add_argument("-partition", help="Partition name") parser.add_argument("-core_root", help="path to CORE_ROOT directory") +parser.add_argument("-run_duration", help="Run duration in minutes") is_windows = platform.system() == "Windows" @@ -71,6 +72,10 @@ def setup_args(args): lambda core_root: os.path.isdir(core_root), "core_root doesn't exist") + coreclr_args.verify(args, + "run_duration", + lambda unused: True, + "Unable to set run_duration") return coreclr_args @@ -164,7 +169,16 @@ def sorter_by_size(pair): except PermissionError as pe_error: print('Ignoring PermissionError: {0}'.format(pe_error)) + src_antigen_log = os.path.join(issues_directory, get_antigen_filename(tag_name)) + dst_antigen_log = os.path.join(upload_directory, get_antigen_filename(tag_name)) + print("Copying {} to {}".format(src_antigen_log, dst_antigen_log)) + try: + shutil.copy2(src_antigen_log, dst_antigen_log) + except PermissionError as pe_error: + print('Ignoring PermissionError: {0}'.format(pe_error)) +def get_antigen_filename(tag_name): + return "Antigen-{}.log".format(tag_name) def main(main_args): """Main entrypoint @@ -179,7 +193,9 @@ def main(main_args): core_root = coreclr_args.core_root tag_name = "{}-{}".format(coreclr_args.run_configuration, coreclr_args.partition) output_directory = coreclr_args.output_directory - run_duration = 120 # Run for 2 hours + run_duration = coreclr_args.run_duration + if not run_duration: + run_duration = 60 path_to_corerun = os.path.join(core_root, "corerun") path_to_tool = os.path.join(antigen_directory, "Antigen") @@ -187,13 +203,17 @@ def main(main_args): path_to_corerun += ".exe" path_to_tool += ".exe" - # Run tool such that issues are placed in a temp folder - with TempDir() as temp_location: - run_command([path_to_tool, "-c", path_to_corerun, "-o", temp_location, "-d", str(run_duration)], _exit_on_fail=True, _long_running= True) - - # Copy issues for upload - print("Copying issues to " + output_directory) - copy_issues(temp_location, output_directory, tag_name) + try: + # Run tool such that issues are placed in a temp folder + with TempDir() as temp_location: + antigen_log = path.join(temp_location, get_antigen_filename(tag_name)) + run_command([path_to_tool, "-c", path_to_corerun, "-o", temp_location, "-d", str(run_duration)], _exit_on_fail=True, _output_file= antigen_log) + + # Copy issues for upload + print("Copying issues to " + output_directory) + copy_issues(temp_location, output_directory, tag_name) + except PermissionError as pe: + print("Got error: %s", pe) if __name__ == "__main__": args = parser.parse_args() diff --git a/src/coreclr/scripts/exploratory.proj b/src/coreclr/scripts/exploratory.proj index 2c347c5a0cbff..8b24de9bdfb52 100644 --- a/src/coreclr/scripts/exploratory.proj +++ b/src/coreclr/scripts/exploratory.proj @@ -28,7 +28,6 @@ false false - 2:30 $(_Creator) $(_HelixAccessToken) $(_HelixBuild) @@ -37,6 +36,16 @@ $(_HelixType) + + + 3:15 + 180 + + + 1:15 + 60 + + @@ -48,7 +57,7 @@ @(HelixPreCommand) @(HelixPostCommand) - $(Python) $(CoreRoot)$(FileSeparatorChar)antigen_run.py -run_configuration $(RunConfiguration) -output_directory $(OutputDirectory) -antigen_directory $(ToolPath) -core_root $(CoreRoot) + $(Python) $(CoreRoot)$(FileSeparatorChar)antigen_run.py -run_configuration $(RunConfiguration) -output_directory $(OutputDirectory) -antigen_directory $(ToolPath) -core_root $(CoreRoot) -run_duration $(RunDuration) @@ -70,7 +79,7 @@ $(WorkItemDirectory) $(WorkItemCommand) -partition %(PartitionName) $(WorkItemTimeout) - AllIssues-$(RunConfiguration)-%(PartitionName).zip;issues-summary-$(RunConfiguration)-%(PartitionName).txt + AllIssues-$(RunConfiguration)-%(PartitionName).zip;issues-summary-$(RunConfiguration)-%(PartitionName).txt;Antigen-$(RunConfiguration)-%(PartitionName).log diff --git a/src/coreclr/scripts/superpmi_setup.py b/src/coreclr/scripts/superpmi_setup.py index a8f2e136ee479..8d4ba0ceeed15 100644 --- a/src/coreclr/scripts/superpmi_setup.py +++ b/src/coreclr/scripts/superpmi_setup.py @@ -312,7 +312,7 @@ def first_fit(sorted_by_size, max_size): return partitions -def run_command(command_to_run, _cwd=None, _exit_on_fail=False, _long_running=False): +def run_command(command_to_run, _cwd=None, _exit_on_fail=False, _output_file=None): """ Runs the command. Args: @@ -320,7 +320,7 @@ def run_command(command_to_run, _cwd=None, _exit_on_fail=False, _long_running=Fa _cwd (string): Current working directory. _exit_on_fail (bool): If it should exit on failure. Returns: - (string, string, int): Returns a tuple of stdout, stderr, and command return code if _long_running= False + (string, string, int): Returns a tuple of stdout, stderr, and command return code if _output_file= None Otherwise stdout, stderr are empty. """ print("Running: " + " ".join(command_to_run)) @@ -328,17 +328,18 @@ def run_command(command_to_run, _cwd=None, _exit_on_fail=False, _long_running=Fa command_stderr = "" return_code = 1 - output_type = subprocess.STDOUT if _long_running else subprocess.PIPE + output_type = subprocess.STDOUT if _output_file else subprocess.PIPE with subprocess.Popen(command_to_run, stdout=subprocess.PIPE, stderr=output_type, cwd=_cwd) as proc: - # For long running command, continuosly print the output - if _long_running: + # For long running command, continuously print the output + if _output_file: while True: - output = proc.stdout.readline() - if proc.poll() is not None: - break - if output: - print(output.strip().decode("utf-8")) + with open(_output_file, 'a') as of: + output = proc.stdout.readline() + if proc.poll() is not None: + break + if output: + of.write(output.strip().decode("utf-8") + "\n") else: command_stdout, command_stderr = proc.communicate() if len(command_stdout) > 0: From e734fdec11bb91197725da87a3fb4b8aef4f0060 Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Tue, 12 Oct 2021 12:34:05 -0700 Subject: [PATCH 049/106] Fix VS4Mac crash report and core dump generation perf problems (#60205) Refactor the DAC enumerate memory region phase out of gather crash info This is so the crash report json is written and available before the core dump which for VS4Mac currently takes 4 minutes. Since on both Linux and MacOS all the RW regions have been already added by createdump itself for heap dumps, then the sometimes slow (4 minutes for VS4Mac) heap enum memory region is changed to the faster normal one. It adds necessary DAC globals, etc. without the costly assembly, module, class, type runtime data structure enumeration. This fast heap dumps is opt'ed in with COMPlus_DbgEnableFastHeapDumps=1 env var to mitigate the risk of something missing from these dumps. Move current_c_gc_state out of !MULTIPLE_HEAPS --- src/coreclr/debug/createdump/crashinfo.cpp | 207 ++++++++++-------- src/coreclr/debug/createdump/crashinfo.h | 9 +- .../debug/createdump/createdumpunix.cpp | 7 +- src/coreclr/debug/daccess/enummem.cpp | 1 + src/coreclr/debug/daccess/request_svr.cpp | 7 +- src/coreclr/gc/gc.cpp | 7 +- 6 files changed, 142 insertions(+), 96 deletions(-) diff --git a/src/coreclr/debug/createdump/crashinfo.cpp b/src/coreclr/debug/createdump/crashinfo.cpp index 72ad06c51b521..70e0007d6925b 100644 --- a/src/coreclr/debug/createdump/crashinfo.cpp +++ b/src/coreclr/debug/createdump/crashinfo.cpp @@ -13,6 +13,8 @@ CrashInfo::CrashInfo(pid_t pid, bool gatherFrames, pid_t crashThread, uint32_t s m_pid(pid), m_ppid(-1), m_hdac(nullptr), + m_pClrDataEnumRegions(nullptr), + m_pClrDataProcess(nullptr), m_gatherFrames(gatherFrames), m_crashThread(crashThread), m_signal(signal), @@ -44,6 +46,15 @@ CrashInfo::~CrashInfo() } m_moduleInfos.clear(); + // Clean up DAC interfaces + if (m_pClrDataEnumRegions != nullptr) + { + m_pClrDataEnumRegions->Release(); + } + if (m_pClrDataProcess != nullptr) + { + m_pClrDataProcess->Release(); + } // Unload DAC module if (m_hdac != nullptr) { @@ -190,8 +201,16 @@ CrashInfo::GatherCrashInfo(MINIDUMP_TYPE minidumpType) thread->GetThreadStack(); } } - // Gather all the useful memory regions from the DAC - if (!EnumerateMemoryRegionsWithDAC(minidumpType)) + // Load and initialize DAC interfaces + if (!InitializeDAC()) + { + return false; + } + if (!EnumerateManagedModules()) + { + return false; + } + if (!UnwindAllThreads()) { return false; } @@ -204,19 +223,15 @@ CrashInfo::GatherCrashInfo(MINIDUMP_TYPE minidumpType) // Enumerate all the memory regions using the DAC memory region support given a minidump type // bool -CrashInfo::EnumerateMemoryRegionsWithDAC(MINIDUMP_TYPE minidumpType) +CrashInfo::InitializeDAC() { ReleaseHolder dataTarget = new DumpDataTarget(*this); PFN_CLRDataCreateInstance pfnCLRDataCreateInstance = nullptr; - ICLRDataEnumMemoryRegions* pClrDataEnumRegions = nullptr; - IXCLRDataProcess* pClrDataProcess = nullptr; - HRESULT hr = S_OK; bool result = false; + HRESULT hr = S_OK; if (!m_coreclrPath.empty()) { - TRACE("EnumerateMemoryRegionsWithDAC: Memory enumeration STARTED\n"); - // We assume that the DAC is in the same location as the libcoreclr.so module std::string dacPath; dacPath.append(m_coreclrPath); @@ -235,140 +250,156 @@ CrashInfo::EnumerateMemoryRegionsWithDAC(MINIDUMP_TYPE minidumpType) fprintf(stderr, "GetProcAddress(CLRDataCreateInstance) FAILED %d\n", GetLastError()); goto exit; } - if ((minidumpType & MiniDumpWithFullMemory) == 0) - { - hr = pfnCLRDataCreateInstance(__uuidof(ICLRDataEnumMemoryRegions), dataTarget, (void**)&pClrDataEnumRegions); - if (FAILED(hr)) - { - fprintf(stderr, "CLRDataCreateInstance(ICLRDataEnumMemoryRegions) FAILED %08x\n", hr); - goto exit; - } - // Calls CrashInfo::EnumMemoryRegion for each memory region found by the DAC - hr = pClrDataEnumRegions->EnumMemoryRegions(this, minidumpType, CLRDATA_ENUM_MEM_DEFAULT); - if (FAILED(hr)) - { - fprintf(stderr, "EnumMemoryRegions FAILED %08x\n", hr); - goto exit; - } - } - hr = pfnCLRDataCreateInstance(__uuidof(IXCLRDataProcess), dataTarget, (void**)&pClrDataProcess); + hr = pfnCLRDataCreateInstance(__uuidof(ICLRDataEnumMemoryRegions), dataTarget, (void**)&m_pClrDataEnumRegions); if (FAILED(hr)) { - fprintf(stderr, "CLRDataCreateInstance(IXCLRDataProcess) FAILED %08x\n", hr); + fprintf(stderr, "CLRDataCreateInstance(ICLRDataEnumMemoryRegions) FAILED %08x\n", hr); goto exit; } - TRACE("EnumerateMemoryRegionsWithDAC: Memory enumeration FINISHED\n"); - if (!EnumerateManagedModules(pClrDataProcess)) + hr = pfnCLRDataCreateInstance(__uuidof(IXCLRDataProcess), dataTarget, (void**)&m_pClrDataProcess); + if (FAILED(hr)) { + fprintf(stderr, "CLRDataCreateInstance(IXCLRDataProcess) FAILED %08x\n", hr); goto exit; } } - else { - TRACE("EnumerateMemoryRegionsWithDAC: coreclr not found; not using DAC\n"); - } - if (!UnwindAllThreads(pClrDataProcess)) + else { - goto exit; + TRACE("InitializeDAC: coreclr not found; not using DAC\n"); } result = true; exit: - if (pClrDataEnumRegions != nullptr) - { - pClrDataEnumRegions->Release(); - } - if (pClrDataProcess != nullptr) + return result; +} + +// +// Enumerate all the memory regions using the DAC memory region support given a minidump type +// +bool +CrashInfo::EnumerateMemoryRegionsWithDAC(MINIDUMP_TYPE minidumpType) +{ + if (m_pClrDataEnumRegions != nullptr && (minidumpType & MiniDumpWithFullMemory) == 0) { - pClrDataProcess->Release(); + TRACE("EnumerateMemoryRegionsWithDAC: Memory enumeration STARTED\n"); + + // Since on both Linux and MacOS all the RW regions will be added for heap + // dumps by createdump, the only thing differentiating a MiniDumpNormal and + // a MiniDumpWithPrivateReadWriteMemory is that the later uses the EnumMemory + // APIs. This is kind of expensive on larger applications (4 minutes, or even + // more), and this should already be in RW pages. Change the dump type to the + // faster normal one. This one already ensures necessary DAC globals, etc. + // without the costly assembly, module, class, type runtime data structures + // enumeration. + if (minidumpType & MiniDumpWithPrivateReadWriteMemory) + { + char* fastHeapDumps = getenv("COMPlus_DbgEnableFastHeapDumps"); + if (fastHeapDumps != nullptr && strcmp(fastHeapDumps, "1") == 0) + { + minidumpType = MiniDumpNormal; + } + } + // Calls CrashInfo::EnumMemoryRegion for each memory region found by the DAC + HRESULT hr = m_pClrDataEnumRegions->EnumMemoryRegions(this, minidumpType, CLRDATA_ENUM_MEM_DEFAULT); + if (FAILED(hr)) + { + fprintf(stderr, "EnumMemoryRegions FAILED %08x\n", hr); + return false; + } + TRACE("EnumerateMemoryRegionsWithDAC: Memory enumeration FINISHED\n"); } - return result; + return true; } // // Enumerate all the managed modules and replace the module mapping with the module name found. // bool -CrashInfo::EnumerateManagedModules(IXCLRDataProcess* pClrDataProcess) +CrashInfo::EnumerateManagedModules() { CLRDATA_ENUM enumModules = 0; - bool result = true; HRESULT hr = S_OK; - if (FAILED(hr = pClrDataProcess->StartEnumModules(&enumModules))) { - fprintf(stderr, "StartEnumModules FAILED %08x\n", hr); - return false; - } - - while (true) + if (m_pClrDataProcess != nullptr) { - ReleaseHolder pClrDataModule; - if ((hr = pClrDataProcess->EnumModule(&enumModules, &pClrDataModule)) != S_OK) { - break; - } + TRACE("EnumerateManagedModules: Module enumeration STARTED\n"); - // Skip any dynamic modules. The Request call below on some DACs crashes on dynamic modules. - ULONG32 flags; - if ((hr = pClrDataModule->GetFlags(&flags)) != S_OK) { - TRACE("MODULE: GetFlags FAILED %08x\n", hr); - continue; - } - if (flags & CLRDATA_MODULE_IS_DYNAMIC) { - TRACE("MODULE: Skipping dynamic module\n"); - continue; + if (FAILED(hr = m_pClrDataProcess->StartEnumModules(&enumModules))) { + fprintf(stderr, "StartEnumModules FAILED %08x\n", hr); + return false; } - DacpGetModuleData moduleData; - if (SUCCEEDED(hr = moduleData.Request(pClrDataModule.GetPtr()))) + while (true) { - TRACE("MODULE: %" PRIA PRIx64 " dyn %d inmem %d file %d pe %" PRIA PRIx64 " pdb %" PRIA PRIx64, (uint64_t)moduleData.LoadedPEAddress, moduleData.IsDynamic, - moduleData.IsInMemory, moduleData.IsFileLayout, (uint64_t)moduleData.PEAssembly, (uint64_t)moduleData.InMemoryPdbAddress); + ReleaseHolder pClrDataModule; + if ((hr = m_pClrDataProcess->EnumModule(&enumModules, &pClrDataModule)) != S_OK) { + break; + } - if (!moduleData.IsDynamic && moduleData.LoadedPEAddress != 0) + // Skip any dynamic modules. The Request call below on some DACs crashes on dynamic modules. + ULONG32 flags; + if ((hr = pClrDataModule->GetFlags(&flags)) != S_OK) { + TRACE("MODULE: GetFlags FAILED %08x\n", hr); + continue; + } + if (flags & CLRDATA_MODULE_IS_DYNAMIC) { + TRACE("MODULE: Skipping dynamic module\n"); + continue; + } + + DacpGetModuleData moduleData; + if (SUCCEEDED(hr = moduleData.Request(pClrDataModule.GetPtr()))) { - ArrayHolder wszUnicodeName = new WCHAR[MAX_LONGPATH + 1]; - if (SUCCEEDED(hr = pClrDataModule->GetFileName(MAX_LONGPATH, nullptr, wszUnicodeName))) + TRACE("MODULE: %" PRIA PRIx64 " dyn %d inmem %d file %d pe %" PRIA PRIx64 " pdb %" PRIA PRIx64, (uint64_t)moduleData.LoadedPEAddress, moduleData.IsDynamic, + moduleData.IsInMemory, moduleData.IsFileLayout, (uint64_t)moduleData.PEAssembly, (uint64_t)moduleData.InMemoryPdbAddress); + + if (!moduleData.IsDynamic && moduleData.LoadedPEAddress != 0) { - std::string moduleName = FormatString("%S", wszUnicodeName.GetPtr()); + ArrayHolder wszUnicodeName = new WCHAR[MAX_LONGPATH + 1]; + if (SUCCEEDED(hr = pClrDataModule->GetFileName(MAX_LONGPATH, nullptr, wszUnicodeName))) + { + std::string moduleName = FormatString("%S", wszUnicodeName.GetPtr()); - // Change the module mapping name - ReplaceModuleMapping(moduleData.LoadedPEAddress, moduleData.LoadedPESize, moduleName); + // Change the module mapping name + ReplaceModuleMapping(moduleData.LoadedPEAddress, moduleData.LoadedPESize, moduleName); - // Add managed module info - AddModuleInfo(true, moduleData.LoadedPEAddress, pClrDataModule, moduleName); + // Add managed module info + AddModuleInfo(true, moduleData.LoadedPEAddress, pClrDataModule, moduleName); + } + else { + TRACE("\nModule.GetFileName FAILED %08x\n", hr); + } } else { - TRACE("\nModule.GetFileName FAILED %08x\n", hr); + TRACE("\n"); } } else { - TRACE("\n"); + TRACE("moduleData.Request FAILED %08x\n", hr); } } - else { - TRACE("moduleData.Request FAILED %08x\n", hr); - } - } - if (enumModules != 0) { - pClrDataProcess->EndEnumModules(enumModules); + if (enumModules != 0) { + m_pClrDataProcess->EndEnumModules(enumModules); + } + TRACE("EnumerateManagedModules: Module enumeration FINISHED\n"); } - - return result; + return true; } // // Unwind all the native threads to ensure that the dwarf unwind info is added to the core dump. // bool -CrashInfo::UnwindAllThreads(IXCLRDataProcess* pClrDataProcess) +CrashInfo::UnwindAllThreads() { ReleaseHolder pSos = nullptr; - if (pClrDataProcess != nullptr) { - pClrDataProcess->QueryInterface(__uuidof(ISOSDacInterface), (void**)&pSos); + if (m_pClrDataProcess != nullptr) { + m_pClrDataProcess->QueryInterface(__uuidof(ISOSDacInterface), (void**)&pSos); } // For each native and managed thread for (ThreadInfo* thread : m_threads) { - if (!thread->UnwindThread(pClrDataProcess, pSos)) { + if (!thread->UnwindThread(m_pClrDataProcess, pSos)) { return false; } } @@ -827,5 +858,5 @@ FormatGuid(const GUID* guid) { uint8_t* bytes = (uint8_t*)guid; return FormatString("%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", - bytes[3], bytes[2], bytes[1], bytes[0], bytes[5], bytes[4], bytes[7], bytes[6], bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]); + bytes[3], bytes[2], bytes[1], bytes[0], bytes[5], bytes[4], bytes[7], bytes[6], bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]); } diff --git a/src/coreclr/debug/createdump/crashinfo.h b/src/coreclr/debug/createdump/crashinfo.h index ca8f77994ac75..e100b7f216102 100644 --- a/src/coreclr/debug/createdump/crashinfo.h +++ b/src/coreclr/debug/createdump/crashinfo.h @@ -47,6 +47,8 @@ class CrashInfo : public ICLRDataEnumMemoryRegionsCallback, pid_t m_ppid; // parent pid pid_t m_tgid; // process group HMODULE m_hdac; // dac module handle when loaded + ICLRDataEnumMemoryRegions* m_pClrDataEnumRegions; // dac enumerate memory interface instance + IXCLRDataProcess* m_pClrDataProcess; // dac process interface instance bool m_gatherFrames; // if true, add the native and managed stack frames to the thread info pid_t m_crashThread; // crashing thread id or 0 if none uint32_t m_signal; // crash signal code or 0 if none @@ -84,6 +86,7 @@ class CrashInfo : public ICLRDataEnumMemoryRegionsCallback, void CleanupAndResumeProcess(); bool EnumerateAndSuspendThreads(); bool GatherCrashInfo(MINIDUMP_TYPE minidumpType); + bool EnumerateMemoryRegionsWithDAC(MINIDUMP_TYPE minidumpType); bool ReadMemory(void* address, void* buffer, size_t size); // read memory and add to dump bool ReadProcessMemory(void* address, void* buffer, size_t size, size_t* read); // read raw memory uint64_t GetBaseAddressFromAddress(uint64_t address); @@ -137,9 +140,9 @@ class CrashInfo : public ICLRDataEnumMemoryRegionsCallback, void VisitProgramHeader(uint64_t loadbias, uint64_t baseAddress, ElfW(Phdr)* phdr); bool EnumerateModuleMappings(); #endif - bool EnumerateMemoryRegionsWithDAC(MINIDUMP_TYPE minidumpType); - bool EnumerateManagedModules(IXCLRDataProcess* pClrDataProcess); - bool UnwindAllThreads(IXCLRDataProcess* pClrDataProcess); + bool InitializeDAC(); + bool EnumerateManagedModules(); + bool UnwindAllThreads(); void ReplaceModuleMapping(CLRDATA_ADDRESS baseAddress, ULONG64 size, const std::string& pszName); void InsertMemoryBackedRegion(const MemoryRegion& region); void InsertMemoryRegion(const MemoryRegion& region); diff --git a/src/coreclr/debug/createdump/createdumpunix.cpp b/src/coreclr/debug/createdump/createdumpunix.cpp index b789c98b820f1..6107adc625efc 100644 --- a/src/coreclr/debug/createdump/createdumpunix.cpp +++ b/src/coreclr/debug/createdump/createdumpunix.cpp @@ -47,7 +47,12 @@ CreateDump(const char* dumpPathTemplate, int pid, const char* dumpType, MINIDUMP CrashReportWriter crashReportWriter(*crashInfo); crashReportWriter.WriteCrashReport(dumpPath); } - printf("Writing %s to file %s\n", dumpType, dumpPath.c_str()); + // Gather all the useful memory regions from the DAC + if (!crashInfo->EnumerateMemoryRegionsWithDAC(minidumpType)) + { + goto exit; + } + fprintf(stdout, "Writing %s to file %s\n", dumpType, dumpPath.c_str()); // Write the actual dump file if (!dumpWriter.OpenDump(dumpPath.c_str())) diff --git a/src/coreclr/debug/daccess/enummem.cpp b/src/coreclr/debug/daccess/enummem.cpp index 1914d13a1c5fb..712f0b35c73f0 100644 --- a/src/coreclr/debug/daccess/enummem.cpp +++ b/src/coreclr/debug/daccess/enummem.cpp @@ -1597,6 +1597,7 @@ HRESULT ClrDataAccess::EnumMemoryRegionsWorkerSkinny(IN CLRDataEnumMemoryFlags f // // collect CLR static CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemCLRStatic(flags); ) + CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemCLRHeapCrticalStatic(flags); ); // Dump AppDomain-specific info needed for MiniDumpNormal. CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpAppDomainInfo(flags); ) diff --git a/src/coreclr/debug/daccess/request_svr.cpp b/src/coreclr/debug/daccess/request_svr.cpp index 4528afe27dab1..b2c09a92c625e 100644 --- a/src/coreclr/debug/daccess/request_svr.cpp +++ b/src/coreclr/debug/daccess/request_svr.cpp @@ -122,8 +122,11 @@ ClrDataAccess::ServerGCHeapDetails(CLRDATA_ADDRESS heapAddr, DacpGcHeapDetails * detailsData->lowest_address = PTR_CDADDR(g_lowest_address); detailsData->highest_address = PTR_CDADDR(g_highest_address); - detailsData->current_c_gc_state = (CLRDATA_ADDRESS)*g_gcDacGlobals->current_c_gc_state; - + detailsData->current_c_gc_state = c_gc_state_free; + if (g_gcDacGlobals->current_c_gc_state != NULL) + { + detailsData->current_c_gc_state = (CLRDATA_ADDRESS)*g_gcDacGlobals->current_c_gc_state; + } // now get information specific to this heap (server mode gives us several heaps; we're getting // information about only one of them. detailsData->alloc_allocated = (CLRDATA_ADDRESS)pHeap->alloc_allocated; diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 32943d2439738..f49d48c76a6e2 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -46708,11 +46708,15 @@ void PopulateDacVars(GcDacVars *gcDacVars) gcDacVars->generation_size = sizeof(generation); gcDacVars->total_generation_count = total_generation_count; gcDacVars->max_gen = &g_max_generation; +#ifdef BACKGROUND_GC + gcDacVars->current_c_gc_state = const_cast(&gc_heap::current_c_gc_state); +#else //BACKGROUND_GC + gcDacVars->current_c_gc_state = 0; +#endif //BACKGROUND_GC #ifndef MULTIPLE_HEAPS gcDacVars->ephemeral_heap_segment = reinterpret_cast(&gc_heap::ephemeral_heap_segment); #ifdef BACKGROUND_GC gcDacVars->mark_array = &gc_heap::mark_array; - gcDacVars->current_c_gc_state = const_cast(&gc_heap::current_c_gc_state); gcDacVars->background_saved_lowest_address = &gc_heap::background_saved_lowest_address; gcDacVars->background_saved_highest_address = &gc_heap::background_saved_highest_address; gcDacVars->next_sweep_obj = &gc_heap::next_sweep_obj; @@ -46725,7 +46729,6 @@ void PopulateDacVars(GcDacVars *gcDacVars) #endif //USE_REGIONS #else //BACKGROUND_GC gcDacVars->mark_array = 0; - gcDacVars->current_c_gc_state = 0; gcDacVars->background_saved_lowest_address = 0; gcDacVars->background_saved_highest_address = 0; gcDacVars->next_sweep_obj = 0; From 68042e15649e8857d72c46071163768654907401 Mon Sep 17 00:00:00 2001 From: Benjamin Hodgson Date: Tue, 12 Oct 2021 17:37:43 -0400 Subject: [PATCH 050/106] Better codegen for `(T)x | (T)y` (#58727) * Better codegen for `(T)x | (T)y` I added a morph pass to fold expressions like `(T)x | (T)y` into `(T)(x | y)`. This results in fewer `movzx` instructions in the asm. Fixes #13816 * Code review updates * Rename function to fgMorphCastedBitwiseOp * Don't fold checked arithmetic * Reuse op1 node for return value * Don't run outside global morphing * Various code style and comment tweaks * Don't call gtGetOp2 if tree was folded. If it was folded, it was folded to a unary (cast) operation and gtGetOp2() will crash. I also tweaked fgMorphCastedBitwiseOp to return nullptr if it didn't do anything (to match behaviour of fgMorphCommutative) * Code review changes for tests * Removed all but one csproj * Added tests for scenarios with overflow, compound exprs, side effects * Add in some asserts and remove a redundant call * fix typo * Code review changes: * Formatting * Use getters instead of fields * set flags on op1 * Fix formatting --- src/coreclr/jit/compiler.h | 1 + src/coreclr/jit/morph.cpp | 80 ++++++++++++++++++ .../JIT/CodeGenBringUpTests/CastThenBinop.cs | 83 +++++++++++++++++++ .../CodeGenBringUpTests/CastThenBinop.csproj | 10 +++ 4 files changed, 174 insertions(+) create mode 100644 src/tests/JIT/CodeGenBringUpTests/CastThenBinop.cs create mode 100644 src/tests/JIT/CodeGenBringUpTests/CastThenBinop.csproj diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 8ea6a7357f5ac..08c064acf7e50 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -6382,6 +6382,7 @@ class Compiler GenTreeLclVar* fgMorphTryFoldObjAsLclVar(GenTreeObj* obj); GenTree* fgMorphCommutative(GenTreeOp* tree); + GenTree* fgMorphCastedBitwiseOp(GenTreeOp* tree); public: GenTree* fgMorphTree(GenTree* tree, MorphAddrContext* mac = nullptr); diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 56bf74801c245..2f6f32593a717 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -10872,6 +10872,70 @@ GenTree* Compiler::fgMorphCommutative(GenTreeOp* tree) return op1; } +//------------------------------------------------------------------------------ +// fgMorphCastedBitwiseOp : Try to simplify "(T)x op (T)y" to "(T)(x op y)". +// +// Arguments: +// tree - node to fold +// +// Return Value: +// A folded GenTree* instance, or nullptr if it couldn't be folded +GenTree* Compiler::fgMorphCastedBitwiseOp(GenTreeOp* tree) +{ + assert(varTypeIsIntegralOrI(tree)); + assert(tree->OperIs(GT_OR, GT_AND, GT_XOR)); + + GenTree* op1 = tree->gtGetOp1(); + GenTree* op2 = tree->gtGetOp2(); + genTreeOps oper = tree->OperGet(); + + // see whether both ops are casts, with matching to and from types. + if (op1->OperIs(GT_CAST) && op2->OperIs(GT_CAST)) + { + // bail if either operand is a checked cast + if (op1->gtOverflow() || op2->gtOverflow()) + { + return nullptr; + } + + var_types fromType = op1->AsCast()->CastOp()->TypeGet(); + var_types toType = op1->AsCast()->CastToType(); + bool isUnsigned = op1->IsUnsigned(); + + if ((op2->CastFromType() != fromType) || (op2->CastToType() != toType) || (op2->IsUnsigned() != isUnsigned)) + { + return nullptr; + } + + // Reuse gentree nodes: + // + // tree op1 + // / \ | + // op1 op2 ==> tree + // | | / \ + // x y x y + // + // (op2 becomes garbage) + + tree->gtOp1 = op1->AsCast()->CastOp(); + tree->gtOp2 = op2->AsCast()->CastOp(); + tree->gtType = fromType; + + op1->gtType = genActualType(toType); + op1->AsCast()->gtOp1 = tree; + op1->AsCast()->CastToType() = toType; + op1->SetAllEffectsFlags(tree); + // no need to update isUnsigned + + DEBUG_DESTROY_NODE(op2); + INDEBUG(op1->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); + + return op1; + } + + return nullptr; +} + /***************************************************************************** * * Transform the given GTK_SMPOP tree for code generation. @@ -12516,6 +12580,22 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac) op2 = tree->AsOp()->gtOp2; } + if (fgGlobalMorph && varTypeIsIntegralOrI(tree) && tree->OperIs(GT_AND, GT_OR, GT_XOR)) + { + GenTree* result = fgMorphCastedBitwiseOp(tree->AsOp()); + if (result != nullptr) + { + assert(result->OperIs(GT_CAST)); + assert(result->AsOp()->gtOp2 == nullptr); + // tree got folded to a unary (cast) op + tree = result; + oper = tree->OperGet(); + typ = tree->TypeGet(); + op1 = tree->AsOp()->gtGetOp1(); + op2 = nullptr; + } + } + if (varTypeIsIntegralOrI(tree->TypeGet()) && tree->OperIs(GT_ADD, GT_MUL, GT_AND, GT_OR, GT_XOR)) { GenTree* foldedTree = fgMorphCommutative(tree->AsOp()); diff --git a/src/tests/JIT/CodeGenBringUpTests/CastThenBinop.cs b/src/tests/JIT/CodeGenBringUpTests/CastThenBinop.cs new file mode 100644 index 0000000000000..959f26114b786 --- /dev/null +++ b/src/tests/JIT/CodeGenBringUpTests/CastThenBinop.cs @@ -0,0 +1,83 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +using System; +using System.Runtime.CompilerServices; + +// Test for https://github.com/dotnet/runtime/issues/13816 +public class Test +{ + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + static int DowncastOr(int a, int b) + { + return (byte)a | (byte)b; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + static long UpcastAnd(int a, int b) + { + return (long)a & (long)b; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + static long UpcastAnd_ComplexExpression(int a, int b) + { + return (long)(a - 2) & (long)(b + 1); + } + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + static long UpcastAnd_SideEffect(int a, int b, out int a1, out int b1) + { + return (long)(a1 = a) & (long)(b1 = b); + } + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + static int DowncastAnd_Overflow(int a, int b) + { + checked + { + return (byte)a & (byte)b; + } + } + + public static int Main() + { + const int Pass = 100; + const int Fail = -1; + + if (DowncastOr(0x0F, 0xF0) != 0xFF) + { + return Fail; + } + if (UpcastAnd(0x0FF, 0xFF0) != 0xF0) + { + return Fail; + } + + try + { + DowncastAnd_Overflow(0x100, 0xFF); + // should throw + return Fail; + } + catch (OverflowException) + { + // expected + } + + { + var result = UpcastAnd_ComplexExpression(0x0FF, 0xFF0); + if (result != 0xF1) + { + return Fail; + } + } + { + var result = UpcastAnd_SideEffect(0x0FF, 0xFF0, out var out1, out var out2); + if (result != 0xF0 || out1 != 0x0FF || out2 != 0xFF0) + { + return Fail; + } + } + + return Pass; + } +} diff --git a/src/tests/JIT/CodeGenBringUpTests/CastThenBinop.csproj b/src/tests/JIT/CodeGenBringUpTests/CastThenBinop.csproj new file mode 100644 index 0000000000000..3d940585af3e0 --- /dev/null +++ b/src/tests/JIT/CodeGenBringUpTests/CastThenBinop.csproj @@ -0,0 +1,10 @@ + + + Exe + None + True + + + + + From 83024a1bf2948c4e4124983e52b8c3d2b56c32ce Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Tue, 12 Oct 2021 15:06:11 -0700 Subject: [PATCH 051/106] Make upgrade install new host first, then uninstall old (#60307) This should preserve the ordering of PATH, since the old MSI will never remove its entry. --- src/installer/pkg/sfx/installers/dotnet-host.proj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/installer/pkg/sfx/installers/dotnet-host.proj b/src/installer/pkg/sfx/installers/dotnet-host.proj index e49489c400582..797d2f5afcc31 100644 --- a/src/installer/pkg/sfx/installers/dotnet-host.proj +++ b/src/installer/pkg/sfx/installers/dotnet-host.proj @@ -11,6 +11,7 @@ sharedhost Dotnet_CLI_SharedHost HostSrc + afterInstallExecute false true sharedhost From e152d75fb77060d308d209768e8da9cc0767eafe Mon Sep 17 00:00:00 2001 From: Maoni Stephens Date: Tue, 12 Oct 2021 15:24:58 -0700 Subject: [PATCH 052/106] fix for GC.GetGCMemoryInfo (#60309) need to re-initialize the generation info each time we fill it in --- src/coreclr/gc/gc.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index f49d48c76a6e2..221d2dc2be631 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -44624,6 +44624,8 @@ bool gc_heap::is_pm_ratio_exceeded() void gc_heap::update_recorded_gen_data (last_recorded_gc_info* gc_info) { + memset (gc_info->gen_info, 0, sizeof (gc_info->gen_info)); + #ifdef MULTIPLE_HEAPS for (int i = 0; i < gc_heap::n_heaps; i++) { From 477789e70e5d179cf6789fa51f2e2fcc3ea2dddd Mon Sep 17 00:00:00 2001 From: SRV Date: Wed, 13 Oct 2021 04:01:04 +0300 Subject: [PATCH 053/106] Fix: CTS.TryReset() concurrency issue #60182 (#60224) * Quick fix of #60182 * Fixed reset condition when there is no timer * Simplify branching --- .../Threading/CancellationTokenSource.cs | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs index 54a7dc691cc0c..3f60a1cd7ff7c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs @@ -392,13 +392,24 @@ public bool TryReset() // to transition from canceled to non-canceled. if (_state == NotCanceledState) { - // If there is no timer, then we're free to reset. If there is a timer, then we need to first try - // to reset it to be infinite so that it won't fire, and then recognize that it could have already - // fired by the time we successfully changed it, and so check to see whether that's possibly the case. - // If we successfully reset it and it never fired, then we can be sure it won't trigger cancellation. - bool reset = - _timer is not TimerQueueTimer timer || - (timer.Change(Timeout.UnsignedInfinite, Timeout.UnsignedInfinite) && !timer._everQueued); + bool reset = false; + + try + { + // If there is no timer, then we're free to reset. If there is a timer, then we need to first try + // to reset it to be infinite so that it won't fire, and then recognize that it could have already + // fired by the time we successfully changed it, and so check to see whether that's possibly the case. + // If we successfully reset it and it never fired, then we can be sure it won't trigger cancellation. + reset = _timer is not TimerQueueTimer timer || + (timer.Change(Timeout.UnsignedInfinite, Timeout.UnsignedInfinite) && !timer._everQueued); + } + catch (ObjectDisposedException) + { + // Just eat the exception. There is no other way to tell that + // the timer has been disposed, and even if there were, there + // would not be a good way to deal with the observe/dispose + // race condition. + } if (reset) { From 6033ef2c56db79e68e13f2856f42d250399d927e Mon Sep 17 00:00:00 2001 From: Maksym Koshovyi Date: Wed, 13 Oct 2021 04:53:51 +0300 Subject: [PATCH 054/106] [Group 3] Enable nullable annotations for `Microsoft.Extensions.Configuration` (#57414) * Annotate src * Annotate ref * Add net6 to Configuration.Abstractions * Fix tests * DisableImplicitAssemblyReferences * TryGet can return null * new() * Fix merge error * InvalidOperationException * Source.Stream DisallowNull * Revert unnecessary changes * More message into resources * Configuration DisallowNull * MaybeNullWhen(false) * Revert "MaybeNullWhen(false)" This reverts commit 48135287366526c05088507923a9a1a3a50d1974. * Remove MaybeNullWhen * NotNullWhen Co-authored-by: Eric Erhardt --- .../ref/Microsoft.Extensions.Configuration.cs | 42 ++++++++++--------- .../Microsoft.Extensions.Configuration.csproj | 7 +++- .../src/ChainedConfigurationProvider.cs | 6 +-- .../src/ChainedConfigurationSource.cs | 5 ++- .../src/ConfigurationKeyComparer.cs | 2 +- .../src/ConfigurationManager.cs | 5 ++- .../src/ConfigurationProvider.cs | 14 +++---- .../src/ConfigurationReloadToken.cs | 2 +- .../src/ConfigurationRoot.cs | 8 ++-- .../src/ConfigurationSection.cs | 7 ++-- .../InternalConfigurationRootExtensions.cs | 2 +- .../MemoryConfigurationBuilderExtensions.cs | 2 +- .../src/MemoryConfigurationProvider.cs | 8 ++-- .../src/MemoryConfigurationSource.cs | 2 +- .../Microsoft.Extensions.Configuration.csproj | 5 ++- .../src/Resources/Strings.resx | 5 ++- .../src/StreamConfigurationProvider.cs | 6 +++ .../src/StreamConfigurationSource.cs | 4 +- ...ionsBuidlerConfigurationExtensionsTests.cs | 8 ++-- 19 files changed, 82 insertions(+), 58 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Configuration/ref/Microsoft.Extensions.Configuration.cs b/src/libraries/Microsoft.Extensions.Configuration/ref/Microsoft.Extensions.Configuration.cs index feef64fc2dde1..c9719bcdbbc92 100644 --- a/src/libraries/Microsoft.Extensions.Configuration/ref/Microsoft.Extensions.Configuration.cs +++ b/src/libraries/Microsoft.Extensions.Configuration/ref/Microsoft.Extensions.Configuration.cs @@ -15,23 +15,24 @@ public partial class ChainedConfigurationProvider : Microsoft.Extensions.Configu { public ChainedConfigurationProvider(Microsoft.Extensions.Configuration.ChainedConfigurationSource source) { } public void Dispose() { } - public System.Collections.Generic.IEnumerable GetChildKeys(System.Collections.Generic.IEnumerable earlierKeys, string parentPath) { throw null; } + public System.Collections.Generic.IEnumerable GetChildKeys(System.Collections.Generic.IEnumerable earlierKeys, string? parentPath) { throw null; } public Microsoft.Extensions.Primitives.IChangeToken GetReloadToken() { throw null; } public void Load() { } - public void Set(string key, string value) { } - public bool TryGet(string key, out string value) { throw null; } + public void Set(string key, string? value) { } + public bool TryGet(string key, out string? value) { throw null; } } public partial class ChainedConfigurationSource : Microsoft.Extensions.Configuration.IConfigurationSource { public ChainedConfigurationSource() { } - public Microsoft.Extensions.Configuration.IConfiguration Configuration { get { throw null; } set { } } + [System.Diagnostics.CodeAnalysis.DisallowNull] + public Microsoft.Extensions.Configuration.IConfiguration? Configuration { get { throw null; } set { } } public bool ShouldDisposeConfiguration { get { throw null; } set { } } public Microsoft.Extensions.Configuration.IConfigurationProvider Build(Microsoft.Extensions.Configuration.IConfigurationBuilder builder) { throw null; } } public sealed partial class ConfigurationManager : Microsoft.Extensions.Configuration.IConfigurationBuilder, Microsoft.Extensions.Configuration.IConfigurationRoot, System.IDisposable { public ConfigurationManager() { } - public string this[string key] { get { throw null; } set { throw null; } } + public string? this[string key] { get { throw null; } set { throw null; } } public IConfigurationSection GetSection(string key) { throw null; } public System.Collections.Generic.IEnumerable GetChildren() { throw null; } public void Dispose() { throw null; } @@ -55,19 +56,19 @@ public partial class ConfigurationKeyComparer : System.Collections.Generic.IComp { public ConfigurationKeyComparer() { } public static Microsoft.Extensions.Configuration.ConfigurationKeyComparer Instance { get { throw null; } } - public int Compare(string x, string y) { throw null; } + public int Compare(string? x, string? y) { throw null; } } public abstract partial class ConfigurationProvider : Microsoft.Extensions.Configuration.IConfigurationProvider { protected ConfigurationProvider() { } - protected System.Collections.Generic.IDictionary Data { get { throw null; } set { } } - public virtual System.Collections.Generic.IEnumerable GetChildKeys(System.Collections.Generic.IEnumerable earlierKeys, string parentPath) { throw null; } + protected System.Collections.Generic.IDictionary Data { get { throw null; } set { } } + public virtual System.Collections.Generic.IEnumerable GetChildKeys(System.Collections.Generic.IEnumerable earlierKeys, string? parentPath) { throw null; } public Microsoft.Extensions.Primitives.IChangeToken GetReloadToken() { throw null; } public virtual void Load() { } protected void OnReload() { } - public virtual void Set(string key, string value) { } + public virtual void Set(string key, string? value) { } public override string ToString() { throw null; } - public virtual bool TryGet(string key, out string value) { throw null; } + public virtual bool TryGet(string key, out string? value) { throw null; } } public partial class ConfigurationReloadToken : Microsoft.Extensions.Primitives.IChangeToken { @@ -75,12 +76,12 @@ public ConfigurationReloadToken() { } public bool ActiveChangeCallbacks { get { throw null; } } public bool HasChanged { get { throw null; } } public void OnReload() { } - public System.IDisposable RegisterChangeCallback(System.Action callback, object state) { throw null; } + public System.IDisposable RegisterChangeCallback(System.Action callback, object? state) { throw null; } } public partial class ConfigurationRoot : Microsoft.Extensions.Configuration.IConfiguration, Microsoft.Extensions.Configuration.IConfigurationRoot, System.IDisposable { public ConfigurationRoot(System.Collections.Generic.IList providers) { } - public string this[string key] { get { throw null; } set { } } + public string? this[string key] { get { throw null; } set { } } public System.Collections.Generic.IEnumerable Providers { get { throw null; } } public void Dispose() { } public System.Collections.Generic.IEnumerable GetChildren() { throw null; } @@ -91,10 +92,10 @@ public void Reload() { } public partial class ConfigurationSection : Microsoft.Extensions.Configuration.IConfiguration, Microsoft.Extensions.Configuration.IConfigurationSection { public ConfigurationSection(Microsoft.Extensions.Configuration.IConfigurationRoot root, string path) { } - public string this[string key] { get { throw null; } set { } } + public string? this[string key] { get { throw null; } set { } } public string Key { get { throw null; } } public string Path { get { throw null; } } - public string Value { get { throw null; } set { } } + public string? Value { get { throw null; } set { } } public System.Collections.Generic.IEnumerable GetChildren() { throw null; } public Microsoft.Extensions.Primitives.IChangeToken GetReloadToken() { throw null; } public Microsoft.Extensions.Configuration.IConfigurationSection GetSection(string key) { throw null; } @@ -102,7 +103,7 @@ public ConfigurationSection(Microsoft.Extensions.Configuration.IConfigurationRoo public static partial class MemoryConfigurationBuilderExtensions { public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddInMemoryCollection(this Microsoft.Extensions.Configuration.IConfigurationBuilder configurationBuilder) { throw null; } - public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddInMemoryCollection(this Microsoft.Extensions.Configuration.IConfigurationBuilder configurationBuilder, System.Collections.Generic.IEnumerable> initialData) { throw null; } + public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddInMemoryCollection(this Microsoft.Extensions.Configuration.IConfigurationBuilder configurationBuilder, System.Collections.Generic.IEnumerable>? initialData) { throw null; } } public abstract partial class StreamConfigurationProvider : Microsoft.Extensions.Configuration.ConfigurationProvider { @@ -114,23 +115,24 @@ public override void Load() { } public abstract partial class StreamConfigurationSource : Microsoft.Extensions.Configuration.IConfigurationSource { protected StreamConfigurationSource() { } - public System.IO.Stream Stream { get { throw null; } set { } } + [System.Diagnostics.CodeAnalysis.DisallowNull] + public System.IO.Stream? Stream { get { throw null; } set { } } public abstract Microsoft.Extensions.Configuration.IConfigurationProvider Build(Microsoft.Extensions.Configuration.IConfigurationBuilder builder); } } namespace Microsoft.Extensions.Configuration.Memory { - public partial class MemoryConfigurationProvider : Microsoft.Extensions.Configuration.ConfigurationProvider, System.Collections.Generic.IEnumerable>, System.Collections.IEnumerable + public partial class MemoryConfigurationProvider : Microsoft.Extensions.Configuration.ConfigurationProvider, System.Collections.Generic.IEnumerable>, System.Collections.IEnumerable { public MemoryConfigurationProvider(Microsoft.Extensions.Configuration.Memory.MemoryConfigurationSource source) { } - public void Add(string key, string value) { } - public System.Collections.Generic.IEnumerator> GetEnumerator() { throw null; } + public void Add(string key, string? value) { } + public System.Collections.Generic.IEnumerator> GetEnumerator() { throw null; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } } public partial class MemoryConfigurationSource : Microsoft.Extensions.Configuration.IConfigurationSource { public MemoryConfigurationSource() { } - public System.Collections.Generic.IEnumerable> InitialData { get { throw null; } set { } } + public System.Collections.Generic.IEnumerable>? InitialData { get { throw null; } set { } } public Microsoft.Extensions.Configuration.IConfigurationProvider Build(Microsoft.Extensions.Configuration.IConfigurationBuilder builder) { throw null; } } } diff --git a/src/libraries/Microsoft.Extensions.Configuration/ref/Microsoft.Extensions.Configuration.csproj b/src/libraries/Microsoft.Extensions.Configuration/ref/Microsoft.Extensions.Configuration.csproj index 311697da63901..0ab0a2842d718 100644 --- a/src/libraries/Microsoft.Extensions.Configuration/ref/Microsoft.Extensions.Configuration.csproj +++ b/src/libraries/Microsoft.Extensions.Configuration/ref/Microsoft.Extensions.Configuration.csproj @@ -1,10 +1,15 @@ - netstandard2.0;$(NetFrameworkMinimum) + $(NetCoreAppCurrent);netstandard2.0;$(NetFrameworkMinimum) + enable + + + + diff --git a/src/libraries/Microsoft.Extensions.Configuration/src/ChainedConfigurationProvider.cs b/src/libraries/Microsoft.Extensions.Configuration/src/ChainedConfigurationProvider.cs index a240f3da8e7d1..eb9bed0021145 100644 --- a/src/libraries/Microsoft.Extensions.Configuration/src/ChainedConfigurationProvider.cs +++ b/src/libraries/Microsoft.Extensions.Configuration/src/ChainedConfigurationProvider.cs @@ -40,7 +40,7 @@ public ChainedConfigurationProvider(ChainedConfigurationSource source) /// The key. /// The value. /// True if a value for the specified key was found, otherwise false. - public bool TryGet(string key, out string value) + public bool TryGet(string key, out string? value) { value = _config[key]; return !string.IsNullOrEmpty(value); @@ -51,7 +51,7 @@ public bool TryGet(string key, out string value) /// /// The key. /// The value. - public void Set(string key, string value) => _config[key] = value; + public void Set(string key, string? value) => _config[key] = value; /// /// Returns a change token if this provider supports change tracking, null otherwise. @@ -74,7 +74,7 @@ public void Load() { } /// The child keys. public IEnumerable GetChildKeys( IEnumerable earlierKeys, - string parentPath) + string? parentPath) { IConfiguration section = parentPath == null ? _config : _config.GetSection(parentPath); var keys = new List(); diff --git a/src/libraries/Microsoft.Extensions.Configuration/src/ChainedConfigurationSource.cs b/src/libraries/Microsoft.Extensions.Configuration/src/ChainedConfigurationSource.cs index 27391aead76fd..b5a9c68c522db 100644 --- a/src/libraries/Microsoft.Extensions.Configuration/src/ChainedConfigurationSource.cs +++ b/src/libraries/Microsoft.Extensions.Configuration/src/ChainedConfigurationSource.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; + namespace Microsoft.Extensions.Configuration { /// @@ -11,7 +13,8 @@ public class ChainedConfigurationSource : IConfigurationSource /// /// The chained configuration. /// - public IConfiguration Configuration { get; set; } + [DisallowNull] + public IConfiguration? Configuration { get; set; } /// /// Whether the chained configuration should be disposed when the diff --git a/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationKeyComparer.cs b/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationKeyComparer.cs index b5dc31fdffe73..00cca781d47f4 100644 --- a/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationKeyComparer.cs +++ b/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationKeyComparer.cs @@ -27,7 +27,7 @@ public class ConfigurationKeyComparer : IComparer /// First string. /// Second string. /// Less than 0 if x is less than y, 0 if x is equal to y and greater than 0 if x is greater than y. - public int Compare(string x, string y) + public int Compare(string? x, string? y) { string[] xParts = x?.Split(_keyDelimiterArray, StringSplitOptions.RemoveEmptyEntries) ?? Array.Empty(); string[] yParts = y?.Split(_keyDelimiterArray, StringSplitOptions.RemoveEmptyEntries) ?? Array.Empty(); diff --git a/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationManager.cs b/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationManager.cs index 8ce08d822426c..6fe4c1a004663 100644 --- a/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationManager.cs +++ b/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationManager.cs @@ -4,6 +4,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using Microsoft.Extensions.Configuration.Memory; @@ -38,7 +39,7 @@ public ConfigurationManager() } /// - public string this[string key] + public string? this[string key] { get { @@ -338,7 +339,7 @@ public bool Remove(KeyValuePair item) return wasRemoved; } - public bool TryGetValue(string key, out object value) + public bool TryGetValue(string key, [NotNullWhen(true)] out object? value) { return _properties.TryGetValue(key, out value); } diff --git a/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationProvider.cs b/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationProvider.cs index e337bd3148331..9fbeded4fc3de 100644 --- a/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationProvider.cs +++ b/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationProvider.cs @@ -21,13 +21,13 @@ public abstract class ConfigurationProvider : IConfigurationProvider /// protected ConfigurationProvider() { - Data = new Dictionary(StringComparer.OrdinalIgnoreCase); + Data = new Dictionary(StringComparer.OrdinalIgnoreCase); } /// /// The configuration key value pairs for this provider. /// - protected IDictionary Data { get; set; } + protected IDictionary Data { get; set; } /// /// Attempts to find a value with the given key, returns true if one is found, false otherwise. @@ -35,7 +35,7 @@ protected ConfigurationProvider() /// The key to lookup. /// The value found at key if one is found. /// True if key has a value, false otherwise. - public virtual bool TryGet(string key, out string value) + public virtual bool TryGet(string key, out string? value) => Data.TryGetValue(key, out value); /// @@ -43,7 +43,7 @@ public virtual bool TryGet(string key, out string value) /// /// The configuration key to set. /// The value to set. - public virtual void Set(string key, string value) + public virtual void Set(string key, string? value) => Data[key] = value; /// @@ -60,13 +60,13 @@ public virtual void Load() /// The list of keys for this provider. public virtual IEnumerable GetChildKeys( IEnumerable earlierKeys, - string parentPath) + string? parentPath) { var results = new List(); if (parentPath is null) { - foreach (KeyValuePair kv in Data) + foreach (KeyValuePair kv in Data) { results.Add(Segment(kv.Key, 0)); } @@ -75,7 +75,7 @@ public virtual IEnumerable GetChildKeys( { Debug.Assert(ConfigurationPath.KeyDelimiter == ":"); - foreach (KeyValuePair kv in Data) + foreach (KeyValuePair kv in Data) { if (kv.Key.Length > parentPath.Length && kv.Key.StartsWith(parentPath, StringComparison.OrdinalIgnoreCase) && diff --git a/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationReloadToken.cs b/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationReloadToken.cs index fad07ebfdacb9..7aceba29fe55b 100644 --- a/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationReloadToken.cs +++ b/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationReloadToken.cs @@ -33,7 +33,7 @@ public class ConfigurationReloadToken : IChangeToken /// The callback to invoke. /// State to be passed into the callback. /// The registration. - public IDisposable RegisterChangeCallback(Action callback, object state) => _cts.Token.Register(callback, state); + public IDisposable RegisterChangeCallback(Action callback, object? state) => _cts.Token.Register(callback, state); /// /// Used to trigger the change token when a reload occurs. diff --git a/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationRoot.cs b/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationRoot.cs index cfe56a20978e5..397ef5160b1ec 100644 --- a/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationRoot.cs +++ b/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationRoot.cs @@ -47,7 +47,7 @@ public ConfigurationRoot(IList providers) /// /// The configuration key. /// The configuration value. - public string this[string key] + public string? this[string key] { get => GetConfiguration(_providers, key); set => SetConfiguration(_providers, key, value); @@ -111,13 +111,13 @@ public void Dispose() } } - internal static string GetConfiguration(IList providers, string key) + internal static string? GetConfiguration(IList providers, string key) { for (int i = providers.Count - 1; i >= 0; i--) { IConfigurationProvider provider = providers[i]; - if (provider.TryGet(key, out string value)) + if (provider.TryGet(key, out string? value)) { return value; } @@ -126,7 +126,7 @@ internal static string GetConfiguration(IList providers, return null; } - internal static void SetConfiguration(IList providers, string key, string value) + internal static void SetConfiguration(IList providers, string key, string? value) { if (providers.Count == 0) { diff --git a/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationSection.cs b/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationSection.cs index da3fbb733a3a1..0896e587d4082 100644 --- a/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationSection.cs +++ b/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationSection.cs @@ -14,7 +14,7 @@ public class ConfigurationSection : IConfigurationSection { private readonly IConfigurationRoot _root; private readonly string _path; - private string _key; + private string? _key; /// /// Initializes a new instance. @@ -61,7 +61,7 @@ public string Key /// /// Gets or sets the section value. /// - public string Value + public string? Value { get { @@ -78,13 +78,12 @@ public string Value /// /// The configuration key. /// The configuration value. - public string this[string key] + public string? this[string key] { get { return _root[ConfigurationPath.Combine(Path, key)]; } - set { _root[ConfigurationPath.Combine(Path, key)] = value; diff --git a/src/libraries/Microsoft.Extensions.Configuration/src/InternalConfigurationRootExtensions.cs b/src/libraries/Microsoft.Extensions.Configuration/src/InternalConfigurationRootExtensions.cs index bff5c52710400..7fd17a98acf47 100644 --- a/src/libraries/Microsoft.Extensions.Configuration/src/InternalConfigurationRootExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Configuration/src/InternalConfigurationRootExtensions.cs @@ -18,7 +18,7 @@ internal static class InternalConfigurationRootExtensions /// Configuration from which to retrieve sub-sections. /// Key of a section of which children to retrieve. /// Immediate children sub-sections of section specified by key. - internal static IEnumerable GetChildrenImplementation(this IConfigurationRoot root, string path) + internal static IEnumerable GetChildrenImplementation(this IConfigurationRoot root, string? path) { return root.Providers .Aggregate(Enumerable.Empty(), diff --git a/src/libraries/Microsoft.Extensions.Configuration/src/MemoryConfigurationBuilderExtensions.cs b/src/libraries/Microsoft.Extensions.Configuration/src/MemoryConfigurationBuilderExtensions.cs index 996f8a876b9c3..15ed46a26a5b9 100644 --- a/src/libraries/Microsoft.Extensions.Configuration/src/MemoryConfigurationBuilderExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Configuration/src/MemoryConfigurationBuilderExtensions.cs @@ -36,7 +36,7 @@ public static IConfigurationBuilder AddInMemoryCollection(this IConfigurationBui /// The . public static IConfigurationBuilder AddInMemoryCollection( this IConfigurationBuilder configurationBuilder, - IEnumerable> initialData) + IEnumerable>? initialData) { if (configurationBuilder == null) { diff --git a/src/libraries/Microsoft.Extensions.Configuration/src/MemoryConfigurationProvider.cs b/src/libraries/Microsoft.Extensions.Configuration/src/MemoryConfigurationProvider.cs index 84a0b142e0fbc..0e7f0be02fec7 100644 --- a/src/libraries/Microsoft.Extensions.Configuration/src/MemoryConfigurationProvider.cs +++ b/src/libraries/Microsoft.Extensions.Configuration/src/MemoryConfigurationProvider.cs @@ -10,7 +10,7 @@ namespace Microsoft.Extensions.Configuration.Memory /// /// In-memory implementation of /// - public class MemoryConfigurationProvider : ConfigurationProvider, IEnumerable> + public class MemoryConfigurationProvider : ConfigurationProvider, IEnumerable> { private readonly MemoryConfigurationSource _source; @@ -29,7 +29,7 @@ public MemoryConfigurationProvider(MemoryConfigurationSource source) if (_source.InitialData != null) { - foreach (KeyValuePair pair in _source.InitialData) + foreach (KeyValuePair pair in _source.InitialData) { Data.Add(pair.Key, pair.Value); } @@ -41,7 +41,7 @@ public MemoryConfigurationProvider(MemoryConfigurationSource source) /// /// The configuration key. /// The configuration value. - public void Add(string key, string value) + public void Add(string key, string? value) { Data.Add(key, value); } @@ -50,7 +50,7 @@ public void Add(string key, string value) /// Returns an enumerator that iterates through the collection. /// /// An enumerator that can be used to iterate through the collection. - public IEnumerator> GetEnumerator() + public IEnumerator> GetEnumerator() { return Data.GetEnumerator(); } diff --git a/src/libraries/Microsoft.Extensions.Configuration/src/MemoryConfigurationSource.cs b/src/libraries/Microsoft.Extensions.Configuration/src/MemoryConfigurationSource.cs index e11891595e414..412df6534c110 100644 --- a/src/libraries/Microsoft.Extensions.Configuration/src/MemoryConfigurationSource.cs +++ b/src/libraries/Microsoft.Extensions.Configuration/src/MemoryConfigurationSource.cs @@ -13,7 +13,7 @@ public class MemoryConfigurationSource : IConfigurationSource /// /// The initial key value configuration pairs. /// - public IEnumerable> InitialData { get; set; } + public IEnumerable>? InitialData { get; set; } /// /// Builds the for this source. diff --git a/src/libraries/Microsoft.Extensions.Configuration/src/Microsoft.Extensions.Configuration.csproj b/src/libraries/Microsoft.Extensions.Configuration/src/Microsoft.Extensions.Configuration.csproj index bc0bf6e7cc443..10fd1c6acdcb6 100644 --- a/src/libraries/Microsoft.Extensions.Configuration/src/Microsoft.Extensions.Configuration.csproj +++ b/src/libraries/Microsoft.Extensions.Configuration/src/Microsoft.Extensions.Configuration.csproj @@ -1,8 +1,11 @@ - netstandard2.0;$(NetFrameworkMinimum) + $(NetCoreAppCurrent);netstandard2.0;$(NetFrameworkMinimum) + enable true + + false Implementation of key-value pair based configuration for Microsoft.Extensions.Configuration. Includes the memory configuration provider. diff --git a/src/libraries/Microsoft.Extensions.Configuration/src/Resources/Strings.resx b/src/libraries/Microsoft.Extensions.Configuration/src/Resources/Strings.resx index 941bf0cc34873..8f6038211e50d 100644 --- a/src/libraries/Microsoft.Extensions.Configuration/src/Resources/Strings.resx +++ b/src/libraries/Microsoft.Extensions.Configuration/src/Resources/Strings.resx @@ -1,4 +1,4 @@ - + + true + + From 6890b50863bb099edd431b1b844a720b074ec5f4 Mon Sep 17 00:00:00 2001 From: LateApexEarlySpeed <72254037+lateapexearlyspeed@users.noreply.github.com> Date: Wed, 13 Oct 2021 13:53:11 +0800 Subject: [PATCH 058/106] lateapexearlyspeed-issue-30778 Create method: TryReadExact(). (#57921) * lateapexearlyspeed-issue-30778 Create method: TryReadExact(). * issue-30778 Add test for SequenceReader.TryReadExact(). * issue-30778 Fix comment: format return xml doc. Co-authored-by: Jeff Handley Co-authored-by: Jeff Handley --- .../System.Memory/ref/System.Memory.cs | 1 + .../System/Buffers/SequenceReader.Search.cs | 26 +++++++++++++++ .../tests/SequenceReader/ReadTo.cs | 33 +++++++++++++++++++ 3 files changed, 60 insertions(+) diff --git a/src/libraries/System.Memory/ref/System.Memory.cs b/src/libraries/System.Memory/ref/System.Memory.cs index 1ddd8df16a6b5..a11ae2b6cf15b 100644 --- a/src/libraries/System.Memory/ref/System.Memory.cs +++ b/src/libraries/System.Memory/ref/System.Memory.cs @@ -314,6 +314,7 @@ public void Rewind(long count) { } public bool TryReadTo(out System.ReadOnlySpan span, T delimiter, T delimiterEscape, bool advancePastDelimiter = true) { throw null; } public bool TryReadToAny(out System.Buffers.ReadOnlySequence sequence, System.ReadOnlySpan delimiters, bool advancePastDelimiter = true) { throw null; } public bool TryReadToAny(out System.ReadOnlySpan span, System.ReadOnlySpan delimiters, bool advancePastDelimiter = true) { throw null; } + public bool TryReadExact(int count, out System.Buffers.ReadOnlySequence sequence) { throw null; } } public readonly partial struct StandardFormat : System.IEquatable { diff --git a/src/libraries/System.Memory/src/System/Buffers/SequenceReader.Search.cs b/src/libraries/System.Memory/src/System/Buffers/SequenceReader.Search.cs index af602ae6ae4a2..71a626dd80ed3 100644 --- a/src/libraries/System.Memory/src/System/Buffers/SequenceReader.Search.cs +++ b/src/libraries/System.Memory/src/System/Buffers/SequenceReader.Search.cs @@ -502,6 +502,32 @@ public bool TryReadTo(out ReadOnlySequence sequence, ReadOnlySpan delimite return false; } + /// + /// Try to read data with given . + /// + /// Read count. + /// The read data, if successfully read requested data. + /// true if remaining items in current is enough for . + public bool TryReadExact(int count, out ReadOnlySequence sequence) + { + if (count < 0) + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count); + } + if (count > Remaining) + { + sequence = default; + return false; + } + + sequence = Sequence.Slice(Position, count); + if (count != 0) + { + Advance(count); + } + return true; + } + /// /// Advance until the given , if found. /// diff --git a/src/libraries/System.Memory/tests/SequenceReader/ReadTo.cs b/src/libraries/System.Memory/tests/SequenceReader/ReadTo.cs index 57ceb9e5985cf..b042eb9d556b8 100644 --- a/src/libraries/System.Memory/tests/SequenceReader/ReadTo.cs +++ b/src/libraries/System.Memory/tests/SequenceReader/ReadTo.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Linq; using System.Buffers; using Xunit; @@ -107,6 +108,38 @@ public void TryReadTo_Sequence(bool advancePastDelimiter, bool useEscapeOverload } } + [Fact] + public void TryReadExact_Sequence() + { + ReadOnlySequence data = SequenceFactory.Create(new int[][] { + new int[] { 0 }, + new int[] { 1, 2 }, + new int[] { }, + new int[] { 3, 4 } + }); + + var sequenceReader = new SequenceReader(data); + + Assert.True(sequenceReader.TryReadExact(0, out ReadOnlySequence sequence)); + Assert.Equal(0, sequence.Length); + + for (int i = 0; i < 2; i++) + { + Assert.True(sequenceReader.TryReadExact(2, out sequence)); + Assert.Equal(Enumerable.Range(i * 2, 2), sequence.ToArray()); + } + + // There is only 1 item in sequence reader + Assert.False(sequenceReader.TryReadExact(2, out _)); + + // The last 1 item was not advanced so still can be fetched + Assert.True(sequenceReader.TryReadExact(1, out sequence)); + Assert.Equal(1, sequence.Length); + Assert.Equal(4, sequence.FirstSpan[0]); + + Assert.True(sequenceReader.End); + } + [Theory, InlineData(false), InlineData(true),] From 082bb6c21f97512bc8f6c7ecb28f43f0e33c8d55 Mon Sep 17 00:00:00 2001 From: Roman Marusyk Date: Wed, 13 Oct 2021 09:09:22 +0300 Subject: [PATCH 059/106] Remove ObjectDisposedException from the docs of the Task.ContinueWith (#59822) --- .../src/System/Threading/Tasks/Future.cs | 12 ------------ .../src/System/Threading/Tasks/Task.cs | 12 ------------ 2 files changed, 24 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs index ac42990b31d1a..72fd3feffff11 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs @@ -618,9 +618,6 @@ public Task ContinueWith(Action> continuationAction) /// /// The argument is null. /// - /// The provided CancellationToken - /// has already been disposed. - /// public Task ContinueWith(Action> continuationAction, CancellationToken cancellationToken) { return ContinueWith(continuationAction, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None); @@ -722,9 +719,6 @@ public Task ContinueWith(Action> continuationAction, TaskContinuat /// /// The argument is null. /// - /// The provided CancellationToken - /// has already been disposed. - /// public Task ContinueWith(Action> continuationAction, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler) { @@ -806,9 +800,6 @@ public Task ContinueWith(Action, object?> continuationAction, obje /// /// The argument is null. /// - /// The provided CancellationToken - /// has already been disposed. - /// public Task ContinueWith(Action, object?> continuationAction, object? state, CancellationToken cancellationToken) { return ContinueWith(continuationAction, state, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None); @@ -913,9 +904,6 @@ public Task ContinueWith(Action, object?> continuationAction, obje /// /// The argument is null. /// - /// The provided CancellationToken - /// has already been disposed. - /// public Task ContinueWith(Action, object?> continuationAction, object? state, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs index 458c914773cbc..7e8888f3e2dc9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs @@ -3553,9 +3553,6 @@ public Task ContinueWith(Action continuationAction) /// /// The argument is null. /// - /// The provided CancellationToken - /// has already been disposed. - /// public Task ContinueWith(Action continuationAction, CancellationToken cancellationToken) { return ContinueWith(continuationAction, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None); @@ -3656,9 +3653,6 @@ public Task ContinueWith(Action continuationAction, TaskContinuationOption /// /// The argument is null. /// - /// The provided CancellationToken - /// has already been disposed. - /// public Task ContinueWith(Action continuationAction, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler) { @@ -3738,9 +3732,6 @@ public Task ContinueWith(Action continuationAction, object? state /// /// The argument is null. /// - /// The provided CancellationToken - /// has already been disposed. - /// public Task ContinueWith(Action continuationAction, object? state, CancellationToken cancellationToken) { return ContinueWith(continuationAction, state, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None); @@ -3844,9 +3835,6 @@ public Task ContinueWith(Action continuationAction, object? state /// /// The argument is null. /// - /// The provided CancellationToken - /// has already been disposed. - /// public Task ContinueWith(Action continuationAction, object? state, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler) { From 09c1a1f7b0c477890b04912d8dd4f742f80faffc Mon Sep 17 00:00:00 2001 From: Theodore Tsirpanis <12659251+teo-tsirpanis@users.noreply.github.com> Date: Wed, 13 Oct 2021 09:26:14 +0300 Subject: [PATCH 060/106] Remove `Microsoft.IO.Redist`. (#58359) * Remove the Microsoft.IO.Redist package. * Remove all conditional compilation directives with MS_IO_REDIST. Co-authored-by: Jeff Handley --- .../IO/FileSystem.Attributes.Windows.cs | 4 - .../FileSystem.DirectoryCreation.Windows.cs | 4 - .../System/IO/PathInternal.CaseSensitivity.cs | 8 - .../Common/src/System/IO/PathInternal.cs | 9 - .../Microsoft.IO.Redist.sln | 42 ---- .../src/Microsoft.IO.Redist.csproj | 221 ------------------ .../src/Microsoft/IO/StringExtensions.cs | 79 ------- .../src/Microsoft/IO/ThrowHelper.cs | 16 -- .../src/Resources/Strings.resx | 125 ---------- .../src/System/IO/Directory.cs | 7 - .../src/System/IO/DirectoryInfo.cs | 7 - .../System/IO/DisableMediaInsertionPrompt.cs | 6 - .../IO/Enumeration/FileSystemEntry.Windows.cs | 4 - .../System/IO/Enumeration/FileSystemEntry.cs | 4 - .../IO/Enumeration/FileSystemEnumerable.cs | 4 - .../FileSystemEnumerableFactory.cs | 4 - .../FileSystemEnumerator.Windows.cs | 4 - .../IO/Enumeration/FileSystemEnumerator.cs | 4 - .../System/IO/Enumeration/FileSystemName.cs | 6 +- .../src/System/IO/EnumerationOptions.cs | 4 - .../src/System/IO/File.cs | 61 ----- .../src/System/IO/FileInfo.cs | 4 - .../src/System/IO/FileSystem.Win32.cs | 6 - .../src/System/IO/FileSystem.Windows.cs | 4 - .../src/System/IO/FileSystem.cs | 5 - .../src/System/IO/FileSystemInfo.Windows.cs | 7 - .../src/System/IO/FileSystemInfo.cs | 4 - .../src/System/IO/MatchCasing.cs | 4 - .../src/System/IO/MatchType.cs | 4 - .../src/System/IO/Path.Windows.cs | 7 - .../src/System/IO/Path.cs | 43 +--- .../src/System/IO/SearchOption.cs | 4 - .../src/System/IO/SearchTarget.cs | 4 - 33 files changed, 3 insertions(+), 716 deletions(-) delete mode 100644 src/libraries/Microsoft.IO.Redist/Microsoft.IO.Redist.sln delete mode 100644 src/libraries/Microsoft.IO.Redist/src/Microsoft.IO.Redist.csproj delete mode 100644 src/libraries/Microsoft.IO.Redist/src/Microsoft/IO/StringExtensions.cs delete mode 100644 src/libraries/Microsoft.IO.Redist/src/Microsoft/IO/ThrowHelper.cs delete mode 100644 src/libraries/Microsoft.IO.Redist/src/Resources/Strings.resx diff --git a/src/libraries/Common/src/System/IO/FileSystem.Attributes.Windows.cs b/src/libraries/Common/src/System/IO/FileSystem.Attributes.Windows.cs index ad087304b4e5a..7e7afcec97a72 100644 --- a/src/libraries/Common/src/System/IO/FileSystem.Attributes.Windows.cs +++ b/src/libraries/Common/src/System/IO/FileSystem.Attributes.Windows.cs @@ -9,11 +9,7 @@ using System.IO; using System.Text; -#if MS_IO_REDIST -namespace Microsoft.IO -#else namespace System.IO -#endif { internal static partial class FileSystem { diff --git a/src/libraries/Common/src/System/IO/FileSystem.DirectoryCreation.Windows.cs b/src/libraries/Common/src/System/IO/FileSystem.DirectoryCreation.Windows.cs index 821f878796e84..dba07a8fb4156 100644 --- a/src/libraries/Common/src/System/IO/FileSystem.DirectoryCreation.Windows.cs +++ b/src/libraries/Common/src/System/IO/FileSystem.DirectoryCreation.Windows.cs @@ -9,11 +9,7 @@ using System.IO; using System.Text; -#if MS_IO_REDIST -namespace Microsoft.IO -#else namespace System.IO -#endif { internal static partial class FileSystem { diff --git a/src/libraries/Common/src/System/IO/PathInternal.CaseSensitivity.cs b/src/libraries/Common/src/System/IO/PathInternal.CaseSensitivity.cs index 80fc6b43ff607..32769debc29d1 100644 --- a/src/libraries/Common/src/System/IO/PathInternal.CaseSensitivity.cs +++ b/src/libraries/Common/src/System/IO/PathInternal.CaseSensitivity.cs @@ -3,10 +3,6 @@ using System.Diagnostics; -#if MS_IO_REDIST -using Microsoft.IO; -#endif - namespace System.IO { /// Contains internal path helpers that are shared between many projects. @@ -28,11 +24,7 @@ internal static bool IsCaseSensitive { get { -#if MS_IO_REDIST - return false; // Windows is always case-insensitive -#else return !(OperatingSystem.IsWindows() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS() || OperatingSystem.IsTvOS() || OperatingSystem.IsWatchOS()); -#endif } } } diff --git a/src/libraries/Common/src/System/IO/PathInternal.cs b/src/libraries/Common/src/System/IO/PathInternal.cs index ff9d79caee253..52aaa59072dd7 100644 --- a/src/libraries/Common/src/System/IO/PathInternal.cs +++ b/src/libraries/Common/src/System/IO/PathInternal.cs @@ -15,13 +15,8 @@ internal static partial class PathInternal /// internal static bool StartsWithDirectorySeparator(ReadOnlySpan path) => path.Length > 0 && IsDirectorySeparator(path[0]); -#if MS_IO_REDIST - internal static string EnsureTrailingSeparator(string path) - => EndsInDirectorySeparator(path) ? path : path + DirectorySeparatorCharAsString; -#else internal static string EnsureTrailingSeparator(string path) => EndsInDirectorySeparator(path.AsSpan()) ? path : path + DirectorySeparatorCharAsString; -#endif internal static bool IsRoot(ReadOnlySpan path) => path.Length == GetRootLength(path); @@ -248,10 +243,6 @@ internal static bool EndsInDirectorySeparator(ReadOnlySpan path) => internal static string GetLinkTargetFullPath(string path, string pathToTarget) => IsPartiallyQualified(pathToTarget.AsSpan()) ? -#if MS_IO_REDIST - Path.Combine(Path.GetDirectoryName(path), pathToTarget) : pathToTarget; -#else Path.Join(Path.GetDirectoryName(path.AsSpan()), pathToTarget.AsSpan()) : pathToTarget; -#endif } } diff --git a/src/libraries/Microsoft.IO.Redist/Microsoft.IO.Redist.sln b/src/libraries/Microsoft.IO.Redist/Microsoft.IO.Redist.sln deleted file mode 100644 index 89c6fe507817a..0000000000000 --- a/src/libraries/Microsoft.IO.Redist/Microsoft.IO.Redist.sln +++ /dev/null @@ -1,42 +0,0 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.IO.Redist", "src\Microsoft.IO.Redist.csproj", "{636F0ACF-D27A-42EB-A080-65BB5E21F4A0}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Runtime.CompilerServices.Unsafe", "..\System.Runtime.CompilerServices.Unsafe\ref\System.Runtime.CompilerServices.Unsafe.csproj", "{2BF3A0D1-DE74-4A55-8074-2B274EBFE805}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Runtime.CompilerServices.Unsafe", "..\System.Runtime.CompilerServices.Unsafe\src\System.Runtime.CompilerServices.Unsafe.ilproj", "{23AFD1C3-1C90-4D14-9B63-A25D82CA39A7}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{408C69F5-70D5-4263-9253-5E83DD9CD764}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{06998809-AB9B-41D0-BDA9-6B70C81A763F}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {636F0ACF-D27A-42EB-A080-65BB5E21F4A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {636F0ACF-D27A-42EB-A080-65BB5E21F4A0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {636F0ACF-D27A-42EB-A080-65BB5E21F4A0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {636F0ACF-D27A-42EB-A080-65BB5E21F4A0}.Release|Any CPU.Build.0 = Release|Any CPU - {2BF3A0D1-DE74-4A55-8074-2B274EBFE805}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2BF3A0D1-DE74-4A55-8074-2B274EBFE805}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2BF3A0D1-DE74-4A55-8074-2B274EBFE805}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2BF3A0D1-DE74-4A55-8074-2B274EBFE805}.Release|Any CPU.Build.0 = Release|Any CPU - {23AFD1C3-1C90-4D14-9B63-A25D82CA39A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {23AFD1C3-1C90-4D14-9B63-A25D82CA39A7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {23AFD1C3-1C90-4D14-9B63-A25D82CA39A7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {23AFD1C3-1C90-4D14-9B63-A25D82CA39A7}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {636F0ACF-D27A-42EB-A080-65BB5E21F4A0} = {408C69F5-70D5-4263-9253-5E83DD9CD764} - {23AFD1C3-1C90-4D14-9B63-A25D82CA39A7} = {408C69F5-70D5-4263-9253-5E83DD9CD764} - {2BF3A0D1-DE74-4A55-8074-2B274EBFE805} = {06998809-AB9B-41D0-BDA9-6B70C81A763F} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {4F40EC53-D415-4145-979B-B592535163C5} - EndGlobalSection -EndGlobal diff --git a/src/libraries/Microsoft.IO.Redist/src/Microsoft.IO.Redist.csproj b/src/libraries/Microsoft.IO.Redist/src/Microsoft.IO.Redist.csproj deleted file mode 100644 index 5c278ee3b83d2..0000000000000 --- a/src/libraries/Microsoft.IO.Redist/src/Microsoft.IO.Redist.csproj +++ /dev/null @@ -1,221 +0,0 @@ - - - true - Downlevel support package for System.IO classes. - $(DefineConstants);MS_IO_REDIST - true - false - annotations - true - net472 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/libraries/Microsoft.IO.Redist/src/Microsoft/IO/StringExtensions.cs b/src/libraries/Microsoft.IO.Redist/src/Microsoft/IO/StringExtensions.cs deleted file mode 100644 index fbe320a857ca0..0000000000000 --- a/src/libraries/Microsoft.IO.Redist/src/Microsoft/IO/StringExtensions.cs +++ /dev/null @@ -1,79 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.CompilerServices; - -namespace Microsoft.IO -{ - public static class StringExtensions - { - public delegate void SpanAction(Span span, TArg arg); - - public static bool Contains(this string s, char value) - { - return s.IndexOf(value) != -1; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool EqualsOrdinal(this ReadOnlySpan span, ReadOnlySpan value) - { - if (span.Length != value.Length) - return false; - if (value.Length == 0) // span.Length == value.Length == 0 - return true; - return span.SequenceEqual(value); - } - - public static unsafe string Create(int length, TState state, SpanAction action) - { - if (action == null) - throw new ArgumentNullException(nameof(action)); - - if (length <= 0) - { - if (length == 0) - return string.Empty; - throw new ArgumentOutOfRangeException(nameof(length)); - } - - string result = new string('\0', length); - fixed (char* r = result) - { - action(new Span(r, length), state); - } - return result; - } - - internal static unsafe string Concat(ReadOnlySpan str0, ReadOnlySpan str1) - { - var result = new string('\0', checked(str0.Length + str1.Length)); - fixed (char* resultPtr = result) - { - var resultSpan = new Span(resultPtr, result.Length); - - str0.CopyTo(resultSpan); - str1.CopyTo(resultSpan.Slice(str0.Length)); - } - return result; - } - - internal static unsafe string Concat(ReadOnlySpan str0, ReadOnlySpan str1, ReadOnlySpan str2) - { - var result = new string('\0', checked(str0.Length + str1.Length + str2.Length)); - fixed (char* resultPtr = result) - { - var resultSpan = new Span(resultPtr, result.Length); - - str0.CopyTo(resultSpan); - resultSpan = resultSpan.Slice(str0.Length); - - str1.CopyTo(resultSpan); - resultSpan = resultSpan.Slice(str1.Length); - - str2.CopyTo(resultSpan); - } - return result; - } - } -} diff --git a/src/libraries/Microsoft.IO.Redist/src/Microsoft/IO/ThrowHelper.cs b/src/libraries/Microsoft.IO.Redist/src/Microsoft/IO/ThrowHelper.cs deleted file mode 100644 index 31e3766c51494..0000000000000 --- a/src/libraries/Microsoft.IO.Redist/src/Microsoft/IO/ThrowHelper.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.IO; - -namespace Microsoft.IO -{ - internal static class ThrowHelper - { - internal static void ThrowEndOfFileException() - { - throw new EndOfStreamException(SR.IO_EOF_ReadBeyondEOF); - } - } -} diff --git a/src/libraries/Microsoft.IO.Redist/src/Resources/Strings.resx b/src/libraries/Microsoft.IO.Redist/src/Resources/Strings.resx deleted file mode 100644 index 66a414fe02e6d..0000000000000 --- a/src/libraries/Microsoft.IO.Redist/src/Resources/Strings.resx +++ /dev/null @@ -1,125 +0,0 @@ - - - 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 - - - The target file '{0}' is a directory, not a file. - - - Invalid File or Directory attributes value. - - - Second path fragment must not be a drive or UNC name. - - - Path must not be a drive. - - - File name cannot be null. - - - Path cannot be null. - - - Enum value was out of legal range. - - - Empty file name is not legal. - - - Empty path name is not legal. - - - Illegal characters in path '{0}'. - - - Invalid seek origin. - - - The directory specified, '{0}', is not a subdirectory of '{1}'. - - - Path cannot be the empty string or all whitespace. - - - Cannot create '{0}' because a file or directory with the same name already exists. - - - The specified directory '{0}' cannot be created. - - - Unable to read beyond the end of the stream. - - - The file '{0}' already exists. - - - Unable to find the specified file. - - - Could not find file '{0}'. - - - The file is too long. This operation is currently limited to supporting files less than 2 gigabytes in size. - - - The link's file system entry type is inconsistent with that of its target: {0} - - - Could not find a part of the path. - - - Could not find a part of the path '{0}'. - - - The specified file name or path is too long, or a component of the specified path is too long. - - - The process cannot access the file '{0}' because it is being used by another process. - - - The process cannot access the file because it is being used by another process. - - - Source and destination path must be different. - - - Source and destination path must have identical roots. Move will not work across volumes. - - - [Unknown] - - - Access to the path is denied. - - - Access to the path '{0}' is denied. - - - File encryption is not supported on this platform. - - - The path '{0}' is too long, or a component of the specified path is too long. - - - Basepath argument is not fully qualified. - - - The path is empty. - - - Drive name must be a root directory (i.e. 'C:\') or a drive letter ('C'). - - - Non-negative number required. - - diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Directory.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Directory.cs index a30543ced5933..2bfcd01604047 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Directory.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Directory.cs @@ -5,16 +5,9 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; - -#if MS_IO_REDIST -using Microsoft.IO.Enumeration; - -namespace Microsoft.IO -#else using System.IO.Enumeration; namespace System.IO -#endif { public static partial class Directory { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/DirectoryInfo.cs b/src/libraries/System.Private.CoreLib/src/System/IO/DirectoryInfo.cs index da8cdec7ab0ee..1aeef2f7501a8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/DirectoryInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/DirectoryInfo.cs @@ -5,16 +5,9 @@ using System.IO; using System.Collections.Generic; using System.Diagnostics; - -#if MS_IO_REDIST -using Microsoft.IO.Enumeration; - -namespace Microsoft.IO -#else using System.IO.Enumeration; namespace System.IO -#endif { public sealed partial class DirectoryInfo : FileSystemInfo { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/DisableMediaInsertionPrompt.cs b/src/libraries/System.Private.CoreLib/src/System/IO/DisableMediaInsertionPrompt.cs index 6510200f2c2fb..6ea4d7893f272 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/DisableMediaInsertionPrompt.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/DisableMediaInsertionPrompt.cs @@ -1,13 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#if MS_IO_REDIST -using System; - -namespace Microsoft.IO -#else namespace System.IO -#endif { /// /// Simple wrapper to safely disable the normal media insertion prompt for diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEntry.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEntry.Windows.cs index 76c0e277f05bd..27a15cb6c192b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEntry.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEntry.Windows.cs @@ -4,11 +4,7 @@ using System; using System.IO; -#if MS_IO_REDIST -namespace Microsoft.IO.Enumeration -#else namespace System.IO.Enumeration -#endif { /// Provides a lower level view of to help process and filter find results. public unsafe ref partial struct FileSystemEntry diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEntry.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEntry.cs index 7ec26cc0f0a44..bea0714fdf211 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEntry.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEntry.cs @@ -4,11 +4,7 @@ using System; using System.IO; -#if MS_IO_REDIST -namespace Microsoft.IO.Enumeration -#else namespace System.IO.Enumeration -#endif { /// Provides a lower level view of to help process and filter find results. public ref partial struct FileSystemEntry diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerable.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerable.cs index 0785e2e10ce4f..3f2e5a3b324ad 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerable.cs @@ -6,11 +6,7 @@ using System.Collections.Generic; using System.Threading; -#if MS_IO_REDIST -namespace Microsoft.IO.Enumeration -#else namespace System.IO.Enumeration -#endif { /// /// Enumerable that allows utilizing custom filter predicates and tranform delegates. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerableFactory.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerableFactory.cs index 378d037bb0fd2..00868756b91d1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerableFactory.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerableFactory.cs @@ -5,11 +5,7 @@ using System.Collections.Generic; using System.IO; -#if MS_IO_REDIST -namespace Microsoft.IO.Enumeration -#else namespace System.IO.Enumeration -#endif { internal static class FileSystemEnumerableFactory { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerator.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerator.Windows.cs index 46b5cbc9bc131..265ac541bed12 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerator.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerator.Windows.cs @@ -11,11 +11,7 @@ using System.Runtime.InteropServices; using System.Threading; -#if MS_IO_REDIST -namespace Microsoft.IO.Enumeration -#else namespace System.IO.Enumeration -#endif { /// Enumerates the file system elements of the provided type that are being searched and filtered by a . public unsafe abstract partial class FileSystemEnumerator : CriticalFinalizerObject, IEnumerator diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerator.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerator.cs index 07dbad8408470..6f22a9729b5d0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerator.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerator.cs @@ -6,11 +6,7 @@ using System.Collections.Generic; using System.Runtime.ConstrainedExecution; -#if MS_IO_REDIST -namespace Microsoft.IO.Enumeration -#else namespace System.IO.Enumeration -#endif { /// Enumerates the file system elements of the provided type that are being searched and filtered by a . /// The type of the result produced by this file system enumerator. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemName.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemName.cs index fc3c83e4837b2..6ee823c5739db 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemName.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemName.cs @@ -4,11 +4,7 @@ using System; using System.Text; -#if MS_IO_REDIST -namespace Microsoft.IO.Enumeration -#else namespace System.IO.Enumeration -#endif { /// Provides methods for matching file system names. public static class FileSystemName @@ -101,7 +97,7 @@ public static bool MatchesSimpleExpression(ReadOnlySpan expression, ReadOn // cannot contain wildcards, while the expression may contain wildcards. // // Expression wild cards are evaluated as shown in the nondeterministic - // finite automatons below. Note that ~* and ~? are DOS_STAR and DOS_QM. + // finite automata below. Note that ~* and ~? are DOS_STAR and DOS_QM. // // ~* is DOS_STAR, ~? is DOS_QM, and ~. is DOS_DOT // diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/EnumerationOptions.cs b/src/libraries/System.Private.CoreLib/src/System/IO/EnumerationOptions.cs index 952d8132488c2..a48d7ce4d0b17 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/EnumerationOptions.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/EnumerationOptions.cs @@ -4,11 +4,7 @@ using System; using System.IO; -#if MS_IO_REDIST -namespace Microsoft.IO -#else namespace System.IO -#endif { /// Provides file and directory enumeration options. public class EnumerationOptions diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/File.cs b/src/libraries/System.Private.CoreLib/src/System/IO/File.cs index 9296fdc0993aa..02d0dfac8f9b6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/File.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/File.cs @@ -12,14 +12,7 @@ using System.Threading.Tasks; using Microsoft.Win32.SafeHandles; -#if MS_IO_REDIST -using System; -using System.IO; - -namespace Microsoft.IO -#else namespace System.IO -#endif { // Class for creating FileStream objects, and some basic file management // routines such as Delete, etc. @@ -340,11 +333,9 @@ public static byte[] ReadAllBytes(string path) } else if (fileLength == 0) { -#if !MS_IO_REDIST // Some file systems (e.g. procfs on Linux) return 0 for length even when there's content. // Thus we need to assume 0 doesn't mean empty. return ReadAllBytesUnknownLength(fs); -#endif } int index = 0; @@ -374,13 +365,8 @@ public static void WriteAllBytes(string path, byte[] bytes) if (bytes == null) throw new ArgumentNullException(nameof(bytes)); -#if MS_IO_REDIST - using FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read); - fs.Write(bytes, 0, bytes.Length); -#else using SafeFileHandle sfh = OpenHandle(path, FileMode.Create, FileAccess.Write, FileShare.Read); RandomAccess.WriteAtOffset(sfh, bytes, 0); -#endif } public static string[] ReadAllLines(string path) { @@ -666,11 +652,7 @@ private static async Task InternalReadAllTextAsync(string path, Encoding StringBuilder sb = new StringBuilder(); while (true) { -#if MS_IO_REDIST - int read = await sr.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false); -#else int read = await sr.ReadAsync(new Memory(buffer), cancellationToken).ConfigureAwait(false); -#endif if (read == 0) { return sb.ToString(); @@ -733,9 +715,7 @@ private static async Task InternalReadAllTextAsync(string path, Encoding if (fileLength > int.MaxValue) { var e = new IOException(SR.IO_FileTooLong2GB); -#if !MS_IO_REDIST ExceptionDispatchInfo.SetCurrentStackTrace(e); -#endif return Task.FromException(e); } @@ -761,11 +741,7 @@ private static async Task InternalReadAllBytesAsync(FileStream fs, int c byte[] bytes = new byte[count]; do { -#if MS_IO_REDIST - int n = await fs.ReadAsync(bytes, index, count - index, cancellationToken).ConfigureAwait(false); -#else int n = await fs.ReadAsync(new Memory(bytes, index, count - index), cancellationToken).ConfigureAwait(false); -#endif if (n == 0) { ThrowHelper.ThrowEndOfFileException(); @@ -804,11 +780,7 @@ private static async Task InternalReadAllBytesUnknownLengthAsync(FileStr } Debug.Assert(bytesRead < rentedArray.Length); -#if MS_IO_REDIST - int n = await fs.ReadAsync(rentedArray, bytesRead, rentedArray.Length - bytesRead, cancellationToken).ConfigureAwait(false); -#else int n = await fs.ReadAsync(rentedArray.AsMemory(bytesRead), cancellationToken).ConfigureAwait(false); -#endif if (n == 0) { return rentedArray.AsSpan(0, bytesRead).ToArray(); @@ -838,13 +810,8 @@ private static async Task InternalReadAllBytesUnknownLengthAsync(FileStr static async Task Core(string path, byte[] bytes, CancellationToken cancellationToken) { -#if MS_IO_REDIST - using FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, 1, FileOptions.Asynchronous); - await fs.WriteAsync(bytes, 0, bytes.Length, cancellationToken).ConfigureAwait(false); -#else using SafeFileHandle sfh = OpenHandle(path, FileMode.Create, FileAccess.Write, FileShare.Read, FileOptions.Asynchronous); await RandomAccess.WriteAtOffsetAsync(sfh, bytes, 0, cancellationToken).ConfigureAwait(false); -#endif } } @@ -924,39 +891,11 @@ private static async Task InternalWriteAllLinesAsync(TextWriter writer, IEnumera private static async Task InternalWriteAllTextAsync(StreamWriter sw, string contents, CancellationToken cancellationToken) { -#if MS_IO_REDIST - char[]? buffer = null; - try - { - buffer = ArrayPool.Shared.Rent(DefaultBufferSize); - int count = contents.Length; - int index = 0; - while (index < count) - { - int batchSize = Math.Min(DefaultBufferSize, count - index); - contents.CopyTo(index, buffer, 0, batchSize); - await sw.WriteAsync(buffer, 0, batchSize).ConfigureAwait(false); - index += batchSize; - } - - cancellationToken.ThrowIfCancellationRequested(); - await sw.FlushAsync().ConfigureAwait(false); - } - finally - { - sw.Dispose(); - if (buffer != null) - { - ArrayPool.Shared.Return(buffer); - } - } -#else using (sw) { await sw.WriteAsync(contents.AsMemory(), cancellationToken).ConfigureAwait(false); await sw.FlushAsync().ConfigureAwait(false); } -#endif } public static Task AppendAllTextAsync(string path, string? contents, CancellationToken cancellationToken = default(CancellationToken)) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileInfo.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileInfo.cs index c7401018a8381..4f42c95ff454f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileInfo.cs @@ -7,11 +7,7 @@ using System.Text; using System.Runtime.Versioning; -#if MS_IO_REDIST -namespace Microsoft.IO -#else namespace System.IO -#endif { // Class for creating FileStream objects, and some basic file management // routines such as Delete, etc. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Win32.cs index 250b98bcc75a8..a9893cd1736c5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Win32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Win32.cs @@ -4,13 +4,7 @@ using System; using System.Runtime.InteropServices; -#if MS_IO_REDIST -using System.IO; - -namespace Microsoft.IO -#else namespace System.IO -#endif { internal static partial class FileSystem { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs index a233347d949ed..b94d7eeb74faf 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs @@ -10,11 +10,7 @@ using System.Text; using System.Buffers; -#if MS_IO_REDIST -namespace Microsoft.IO -#else namespace System.IO -#endif { internal static partial class FileSystem { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.cs index a1d506e9218c8..09f6000326111 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.cs @@ -1,12 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#if MS_IO_REDIST -using System; -namespace Microsoft.IO -#else namespace System.IO -#endif { internal static partial class FileSystem { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystemInfo.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystemInfo.Windows.cs index bb1440851733c..cadd69ce7e67a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystemInfo.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystemInfo.Windows.cs @@ -4,16 +4,9 @@ using System; using System.Diagnostics; using System.IO; - -#if MS_IO_REDIST -using Microsoft.IO.Enumeration; - -namespace Microsoft.IO -#else using System.IO.Enumeration; namespace System.IO -#endif { public partial class FileSystemInfo { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystemInfo.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystemInfo.cs index 3beed9e25635e..eb4111efee2b2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystemInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystemInfo.cs @@ -5,11 +5,7 @@ using System.IO; using System.Runtime.Serialization; -#if MS_IO_REDIST -namespace Microsoft.IO -#else namespace System.IO -#endif { public abstract partial class FileSystemInfo : MarshalByRefObject, ISerializable { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/MatchCasing.cs b/src/libraries/System.Private.CoreLib/src/System/IO/MatchCasing.cs index 607ebe54c2813..0567042cdd7a1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/MatchCasing.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/MatchCasing.cs @@ -1,11 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#if MS_IO_REDIST -namespace Microsoft.IO -#else namespace System.IO -#endif { /// Specifies the type of character casing to match. public enum MatchCasing diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/MatchType.cs b/src/libraries/System.Private.CoreLib/src/System/IO/MatchType.cs index 857796c938255..d157b13ad5ac4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/MatchType.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/MatchType.cs @@ -1,11 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#if MS_IO_REDIST -namespace Microsoft.IO -#else namespace System.IO -#endif { /// Specifies the type of wildcard matching to use. public enum MatchType 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 e0ad5c0c26af2..b10cedd4ecbdd 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 @@ -5,14 +5,7 @@ using System.Diagnostics.CodeAnalysis; using System.Text; -#if MS_IO_REDIST -using System; -using System.IO; - -namespace Microsoft.IO -#else namespace System.IO -#endif { public static partial class Path { 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 49623b583e640..3732397f17ba0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs @@ -6,14 +6,7 @@ using System.Runtime.InteropServices; using System.Text; -#if MS_IO_REDIST -using System; -using System.IO; - -namespace Microsoft.IO -#else namespace System.IO -#endif { // Provides methods for processing file system strings in a cross-platform manner. // Most of the methods don't do a complete parsing (such as examining a UNC hostname), @@ -77,15 +70,9 @@ public static partial class Path } ReadOnlySpan subpath = path.AsSpan(0, subLength); -#if MS_IO_REDIST - return extension.Length != 0 && extension[0] == '.' ? - StringExtensions.Concat(subpath, extension.AsSpan()) : - StringExtensions.Concat(subpath, ".".AsSpan(), extension.AsSpan()); -#else return extension.StartsWith('.') ? string.Concat(subpath, extension) : string.Concat(subpath, ".", extension); -#endif } /// @@ -253,11 +240,7 @@ public static unsafe string GetRandomFileName() byte* pKey = stackalloc byte[KeyLength]; Interop.GetRandomBytes(pKey, KeyLength); -#if MS_IO_REDIST - return StringExtensions.Create( -#else return string.Create( -#endif 12, (IntPtr)pKey, (span, key) => // 12 == 8 + 1 (for period) + 3 Populate83FileNameFromRandomBytes((byte*)key, KeyLength, span)); } @@ -645,25 +628,9 @@ private static unsafe string JoinInternal(ReadOnlySpan first, ReadOnlySpan bool hasSeparator = PathInternal.IsDirectorySeparator(first[first.Length - 1]) || PathInternal.IsDirectorySeparator(second[0]); -#if !MS_IO_REDIST return hasSeparator ? string.Concat(first, second) : string.Concat(first, PathInternal.DirectorySeparatorCharAsString, second); -#else - fixed (char* f = &MemoryMarshal.GetReference(first), s = &MemoryMarshal.GetReference(second)) - { - return StringExtensions.Create( - first.Length + second.Length + (hasSeparator ? 0 : 1), - (First: (IntPtr)f, FirstLength: first.Length, Second: (IntPtr)s, SecondLength: second.Length), - static (destination, state) => - { - new Span((char*)state.First, state.FirstLength).CopyTo(destination); - if (destination.Length != (state.FirstLength + state.SecondLength)) - destination[state.FirstLength] = PathInternal.DirectorySeparatorChar; - new Span((char*)state.Second, state.SecondLength).CopyTo(destination.Slice(destination.Length - state.SecondLength)); - }); - } -#endif } private unsafe readonly struct Join3Payload @@ -702,11 +669,8 @@ private static unsafe string JoinInternal(ReadOnlySpan first, ReadOnlySpan var payload = new Join3Payload( f, first.Length, s, second.Length, t, third.Length, (byte)(firstNeedsSeparator | secondNeedsSeparator << 1)); -#if MS_IO_REDIST - return StringExtensions.Create( -#else + return string.Create( -#endif first.Length + second.Length + third.Length + firstNeedsSeparator + secondNeedsSeparator, (IntPtr)(&payload), static (destination, statePtr) => @@ -765,11 +729,8 @@ private static unsafe string JoinInternal(ReadOnlySpan first, ReadOnlySpan var payload = new Join4Payload( f, first.Length, s, second.Length, t, third.Length, u, fourth.Length, (byte)(firstNeedsSeparator | secondNeedsSeparator << 1 | thirdNeedsSeparator << 2)); -#if MS_IO_REDIST - return StringExtensions.Create( -#else + return string.Create( -#endif first.Length + second.Length + third.Length + fourth.Length + firstNeedsSeparator + secondNeedsSeparator + thirdNeedsSeparator, (IntPtr)(&payload), static (destination, statePtr) => diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/SearchOption.cs b/src/libraries/System.Private.CoreLib/src/System/IO/SearchOption.cs index c7fd2be70b68a..b37681889ada3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/SearchOption.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/SearchOption.cs @@ -1,11 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#if MS_IO_REDIST -namespace Microsoft.IO -#else namespace System.IO -#endif { /// /// Enum describing whether the search operation should diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/SearchTarget.cs b/src/libraries/System.Private.CoreLib/src/System/IO/SearchTarget.cs index a1bed2ae7ff16..2b1bc9d89feac 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/SearchTarget.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/SearchTarget.cs @@ -1,11 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#if MS_IO_REDIST -namespace Microsoft.IO -#else namespace System.IO -#endif { internal enum SearchTarget { From 845e66cd08240dc77bce98f40cecaaa0f0dadc65 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Wed, 13 Oct 2021 11:40:04 +0300 Subject: [PATCH 061/106] JIT: Optimize redundant memory barriers on arm/arm64 (#60219) --- src/coreclr/jit/codegenarm.cpp | 32 ++++++++++++++++++++ src/coreclr/jit/codegenarm64.cpp | 50 ++++++++++++++++++++++++++++++++ src/coreclr/jit/codegenxarch.cpp | 26 +++++++++++++++++ src/coreclr/jit/emit.cpp | 20 +++++++++++++ src/coreclr/jit/emit.h | 4 +++ src/coreclr/jit/instr.cpp | 35 ---------------------- src/coreclr/jit/instr.h | 4 +++ 7 files changed, 136 insertions(+), 35 deletions(-) diff --git a/src/coreclr/jit/codegenarm.cpp b/src/coreclr/jit/codegenarm.cpp index 0ae592f0bdbc2..151ea25e2bf22 100644 --- a/src/coreclr/jit/codegenarm.cpp +++ b/src/coreclr/jit/codegenarm.cpp @@ -1883,4 +1883,36 @@ void CodeGen::genAllocLclFrame(unsigned frameSize, regNumber initReg, bool* pIni #endif // USING_SCOPE_INFO } +//----------------------------------------------------------------------------------- +// instGen_MemoryBarrier: Emit a MemoryBarrier instruction +// +// Arguments: +// barrierKind - kind of barrier to emit (ignored on arm32) +// +// Notes: +// All MemoryBarriers instructions can be removed by DOTNET_JitNoMemoryBarriers=1 +// barrierKind argument is ignored on arm32 and a full memory barrier is emitted +// +void CodeGen::instGen_MemoryBarrier(BarrierKind barrierKind) +{ +#ifdef DEBUG + if (JitConfig.JitNoMemoryBarriers() == 1) + { + return; + } +#endif // DEBUG + + // Avoid emitting redundant memory barriers on arm32 if they belong to the same IG + // and there were no memory accesses in-between them + if ((GetEmitter()->emitLastMemBarrier != nullptr) && compiler->opts.OptimizationEnabled()) + { + assert(GetEmitter()->emitLastMemBarrier->idSmallCns() == INS_BARRIER_SY); + } + else + { + // ARM has only full barriers, so all barriers need to be emitted as full. + GetEmitter()->emitIns_I(INS_dmb, EA_4BYTE, INS_BARRIER_SY); + } +} + #endif // TARGET_ARM diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp index 0d4d9e9da2c20..fb88df61c6c64 100644 --- a/src/coreclr/jit/codegenarm64.cpp +++ b/src/coreclr/jit/codegenarm64.cpp @@ -9510,4 +9510,54 @@ void CodeGen::genAllocLclFrame(unsigned frameSize, regNumber initReg, bool* pIni } } +//----------------------------------------------------------------------------------- +// instGen_MemoryBarrier: Emit a MemoryBarrier instruction +// +// Arguments: +// barrierKind - kind of barrier to emit (Full or Load-Only). +// +// Notes: +// All MemoryBarriers instructions can be removed by DOTNET_JitNoMemoryBarriers=1 +// +void CodeGen::instGen_MemoryBarrier(BarrierKind barrierKind) +{ +#ifdef DEBUG + if (JitConfig.JitNoMemoryBarriers() == 1) + { + return; + } +#endif // DEBUG + + // Avoid emitting redundant memory barriers on arm64 if they belong to the same IG + // and there were no memory accesses in-between them + emitter::instrDesc* lastMemBarrier = GetEmitter()->emitLastMemBarrier; + if ((lastMemBarrier != nullptr) && compiler->opts.OptimizationEnabled()) + { + BarrierKind prevBarrierKind = BARRIER_FULL; + if (lastMemBarrier->idSmallCns() == INS_BARRIER_ISHLD) + { + prevBarrierKind = BARRIER_LOAD_ONLY; + } + else + { + // Currently we only emit two kinds of barriers on arm64: + // ISH - Full (inner shareable domain) + // ISHLD - LoadOnly (inner shareable domain) + assert(lastMemBarrier->idSmallCns() == INS_BARRIER_ISH); + } + + if ((prevBarrierKind == BARRIER_LOAD_ONLY) && (barrierKind == BARRIER_FULL)) + { + // Previous memory barrier: load-only, current: full + // Upgrade the previous one to full + assert((prevBarrierKind == BARRIER_LOAD_ONLY) && (barrierKind == BARRIER_FULL)); + lastMemBarrier->idSmallCns(INS_BARRIER_ISH); + } + } + else + { + GetEmitter()->emitIns_BARR(INS_dmb, barrierKind == BARRIER_LOAD_ONLY ? INS_BARRIER_ISHLD : INS_BARRIER_ISH); + } +} + #endif // TARGET_ARM64 diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index 9c280d6e63977..721f95006ba28 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -9135,4 +9135,30 @@ void CodeGen::genPushCalleeSavedRegisters() } } +//----------------------------------------------------------------------------------- +// instGen_MemoryBarrier: Emit a MemoryBarrier instruction +// +// Arguments: +// barrierKind - kind of barrier to emit (Load-only is no-op on xarch) +// +// Notes: +// All MemoryBarriers instructions can be removed by DOTNET_JitNoMemoryBarriers=1 +// +void CodeGen::instGen_MemoryBarrier(BarrierKind barrierKind) +{ +#ifdef DEBUG + if (JitConfig.JitNoMemoryBarriers() == 1) + { + return; + } +#endif // DEBUG + + // only full barrier needs to be emitted on Xarch + if (barrierKind == BARRIER_FULL) + { + instGen(INS_lock); + GetEmitter()->emitIns_I_AR(INS_or, EA_4BYTE, 0, REG_SPBASE, 0); + } +} + #endif // TARGET_XARCH diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index 8bf8e586f34e1..3708f5123a942 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -703,6 +703,11 @@ insGroup* emitter::emitSavIG(bool emitAdd) ig = emitCurIG; assert(ig); +#ifdef TARGET_ARMARCH + // Reset emitLastMemBarrier for new IG + emitLastMemBarrier = nullptr; +#endif + // Compute how much code we've generated sz = emitCurIGfreeNext - emitCurIGfreeBase; @@ -1140,6 +1145,10 @@ void emitter::emitBegFN(bool hasFramePtr emitLastIns = nullptr; +#ifdef TARGET_ARMARCH + emitLastMemBarrier = nullptr; +#endif + ig->igNext = nullptr; #ifdef DEBUG @@ -1303,6 +1312,17 @@ void emitter::dispIns(instrDesc* id) void emitter::appendToCurIG(instrDesc* id) { +#ifdef TARGET_ARMARCH + if (id->idIns() == INS_dmb) + { + emitLastMemBarrier = id; + } + else if (emitInsIsLoadOrStore(id->idIns())) + { + // A memory access - reset saved memory barrier + emitLastMemBarrier = nullptr; + } +#endif emitCurIGsize += id->idCodeSize(); } diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index d84ede9ed63e8..23d5e0327db57 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -1884,6 +1884,10 @@ class emitter instrDesc* emitLastIns; +#ifdef TARGET_ARMARCH + instrDesc* emitLastMemBarrier; +#endif + #ifdef DEBUG void emitCheckIGoffsets(); #endif diff --git a/src/coreclr/jit/instr.cpp b/src/coreclr/jit/instr.cpp index d15a17118f9a1..39f885d925fdf 100644 --- a/src/coreclr/jit/instr.cpp +++ b/src/coreclr/jit/instr.cpp @@ -2386,41 +2386,6 @@ void CodeGen::instGen_Return(unsigned stkArgSize) #endif } -/***************************************************************************** - * - * Emit a MemoryBarrier instruction - * - * Note: all MemoryBarriers instructions can be removed by - * SET COMPlus_JitNoMemoryBarriers=1 - */ -void CodeGen::instGen_MemoryBarrier(BarrierKind barrierKind) -{ -#ifdef DEBUG - if (JitConfig.JitNoMemoryBarriers() == 1) - { - return; - } -#endif // DEBUG - -#if defined(TARGET_XARCH) - // only full barrier needs to be emitted on Xarch - if (barrierKind != BARRIER_FULL) - { - return; - } - - instGen(INS_lock); - GetEmitter()->emitIns_I_AR(INS_or, EA_4BYTE, 0, REG_SPBASE, 0); -#elif defined(TARGET_ARM) - // ARM has only full barriers, so all barriers need to be emitted as full. - GetEmitter()->emitIns_I(INS_dmb, EA_4BYTE, 0xf); -#elif defined(TARGET_ARM64) - GetEmitter()->emitIns_BARR(INS_dmb, barrierKind == BARRIER_LOAD_ONLY ? INS_BARRIER_ISHLD : INS_BARRIER_ISH); -#else -#error "Unknown TARGET" -#endif -} - /***************************************************************************** * * Machine independent way to move a Zero value into a register diff --git a/src/coreclr/jit/instr.h b/src/coreclr/jit/instr.h index d9e2b9319ee4c..ca2a00ff27b12 100644 --- a/src/coreclr/jit/instr.h +++ b/src/coreclr/jit/instr.h @@ -161,6 +161,10 @@ enum insOpts: unsigned INS_OPTS_ASR, INS_OPTS_ROR }; +enum insBarrier : unsigned +{ + INS_BARRIER_SY = 15 +}; #elif defined(TARGET_ARM64) enum insOpts : unsigned { From d01cb54a92a0227ce1dd7e7b17d4b39f00486799 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Wed, 13 Oct 2021 15:49:30 +0300 Subject: [PATCH 062/106] [mono][interp] Change GC state explicitly (#60242) * [interp] Change GC state explicitly GC transitions are being called from the m2n wrapper and they expect a LMF to have been pushed. With interpreter however, the LMF is not pushed during the execution of the wrapper but as part of the native CALLI. This means we need to do the GC transitions at a later time, when the LMF is pushed. We detect the GC transition IL code in the wrapper and instead of emitting the actual code we mark a flag. This flag is checked during the CALLI operations. * [interp] Remove check for gc unsafe state during exception checkpoint We are always in gc unsafe once returning from pinvokes or icalls. --- src/mono/mono/mini/interp/interp.c | 81 ++++++++++++++------------- src/mono/mono/mini/interp/mintops.def | 2 +- src/mono/mono/mini/interp/transform.c | 24 ++++++-- 3 files changed, 63 insertions(+), 44 deletions(-) diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index be586a546ed86..abd3e25e65a15 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -1037,16 +1037,6 @@ interp_throw (ThreadContext *context, MonoException *ex, InterpFrame *frame, con } \ } while (0) -/* Don't throw exception if thread is in GC Safe mode. Should only happen in managed-to-native wrapper. */ -#define EXCEPTION_CHECKPOINT_GC_UNSAFE \ - do { \ - if (mono_thread_interruption_request_flag && !mono_threads_is_critical_method (frame->imethod->method) && mono_thread_is_gc_unsafe_mode ()) { \ - MonoException *exc = mono_thread_interruption_checkpoint (); \ - if (exc) \ - THROW_EX_GENERAL (exc, ip, TRUE); \ - } \ - } while (0) - // Reduce duplicate code in interp_exec_method static void do_safepoint (InterpFrame *frame, ThreadContext *context) @@ -1530,7 +1520,8 @@ ves_pinvoke_method ( stackval *ret_sp, stackval *sp, gboolean save_last_error, - gpointer *cache) + gpointer *cache, + gboolean *gc_transitions) { InterpFrame frame = {0}; frame.parent = parent_frame; @@ -1541,14 +1532,7 @@ ves_pinvoke_method ( MonoLMFExt ext; gpointer args; - /* - * When there's a calli in a pinvoke wrapper, we're in GC Safe mode. - * When we're called for some other calli, we may be in GC Unsafe mode. - * - * On any code path where we call anything other than the entry_func, - * we need to switch back to GC Unsafe before calling the runtime. - */ - MONO_REQ_GC_NEUTRAL_MODE; + MONO_REQ_GC_UNSAFE_MODE; #ifdef HOST_WASM /* @@ -1584,9 +1568,7 @@ ves_pinvoke_method ( #ifdef MONO_ARCH_HAVE_INTERP_PINVOKE_TRAMP CallContext ccontext; - MONO_ENTER_GC_UNSAFE; mono_arch_set_native_call_context_args (&ccontext, &frame, sig); - MONO_EXIT_GC_UNSAFE; args = &ccontext; #else InterpMethodArguments *margs = build_args_from_sig (sig, &frame); @@ -1594,16 +1576,23 @@ ves_pinvoke_method ( #endif INTERP_PUSH_LMF_WITH_CTX (&frame, ext, exit_pinvoke); - entry_func ((gpointer) addr, args); + + if (*gc_transitions) { + MONO_ENTER_GC_SAFE; + entry_func ((gpointer) addr, args); + MONO_EXIT_GC_SAFE; + *gc_transitions = FALSE; + } else { + entry_func ((gpointer) addr, args); + } + if (save_last_error) mono_marshal_set_last_error (); interp_pop_lmf (&ext); #ifdef MONO_ARCH_HAVE_INTERP_PINVOKE_TRAMP if (!context->has_resume_state) { - MONO_ENTER_GC_UNSAFE; mono_arch_get_native_call_context_ret (&ccontext, &frame, sig); - MONO_EXIT_GC_UNSAFE; } g_free (ccontext.stack); @@ -1965,7 +1954,9 @@ interp_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObject // method is transformed. context->stack_pointer = (guchar*)(sp + 4); + MONO_ENTER_GC_UNSAFE; interp_exec_method (&frame, context, NULL); + MONO_EXIT_GC_UNSAFE; context->stack_pointer = (guchar*)sp; @@ -2047,7 +2038,9 @@ interp_entry (InterpEntryData *data) context->stack_pointer = (guchar*)sp_args; + MONO_ENTER_GC_UNSAFE; interp_exec_method (&frame, context, NULL); + MONO_EXIT_GC_UNSAFE; context->stack_pointer = (guchar*)sp; @@ -2179,12 +2172,19 @@ do_icall (MonoMethodSignature *sig, int op, stackval *ret_sp, stackval *sp, gpoi #endif // Do not inline in case order of frame addresses matters, and maybe other reasons. static MONO_NO_OPTIMIZATION MONO_NEVER_INLINE gpointer -do_icall_wrapper (InterpFrame *frame, MonoMethodSignature *sig, int op, stackval *ret_sp, stackval *sp, gpointer ptr, gboolean save_last_error) +do_icall_wrapper (InterpFrame *frame, MonoMethodSignature *sig, int op, stackval *ret_sp, stackval *sp, gpointer ptr, gboolean save_last_error, gboolean *gc_transitions) { MonoLMFExt ext; INTERP_PUSH_LMF_WITH_CTX (frame, ext, exit_icall); - do_icall (sig, op, ret_sp, sp, ptr, save_last_error); + if (*gc_transitions) { + MONO_ENTER_GC_SAFE; + do_icall (sig, op, ret_sp, sp, ptr, save_last_error); + MONO_EXIT_GC_SAFE; + *gc_transitions = FALSE; + } else { + do_icall (sig, op, ret_sp, sp, ptr, save_last_error); + } interp_pop_lmf (&ext); @@ -2745,7 +2745,9 @@ interp_entry_from_trampoline (gpointer ccontext_untyped, gpointer rmethod_untype } context->stack_pointer = (guchar*)newsp; + MONO_ENTER_GC_UNSAFE; interp_exec_method (&frame, context, NULL); + MONO_EXIT_GC_UNSAFE; context->stack_pointer = (guchar*)sp; g_assert (!context->has_resume_state); @@ -3129,14 +3131,14 @@ static MONO_NEVER_INLINE MonoException* mono_interp_leave (InterpFrame* parent_frame) { InterpFrame frame = {parent_frame}; - + gboolean gc_transitions = FALSE; stackval tmp_sp; /* * We need for mono_thread_get_undeniable_exception to be able to unwind * to check the abort threshold. For this to work we use frame as a * dummy frame that is stored in the lmf and serves as the transition frame */ - do_icall_wrapper (&frame, NULL, MINT_ICALL_V_P, &tmp_sp, &tmp_sp, (gpointer)mono_thread_get_undeniable_exception, FALSE); + do_icall_wrapper (&frame, NULL, MINT_ICALL_V_P, &tmp_sp, &tmp_sp, (gpointer)mono_thread_get_undeniable_exception, FALSE, &gc_transitions); return (MonoException*)tmp_sp.data.p; } @@ -3244,6 +3246,7 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs unsigned char *locals = NULL; int call_args_offset; int return_offset; + gboolean gc_transitions = FALSE; #if DEBUG_INTERP int tracing = global_tracing; @@ -3536,8 +3539,8 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs /* for calls, have ip pointing at the start of next instruction */ frame->state.ip = ip + 7; - do_icall_wrapper (frame, csignature, opcode, ret, args, target_ip, save_last_error); - EXCEPTION_CHECKPOINT_GC_UNSAFE; + do_icall_wrapper (frame, csignature, opcode, ret, args, target_ip, save_last_error, &gc_transitions); + EXCEPTION_CHECKPOINT; CHECK_RESUME_STATE (context); ip += 7; MINT_IN_BREAK; @@ -3564,9 +3567,9 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs gpointer *cache = (gpointer*)&frame->imethod->data_items [ip [7]]; /* for calls, have ip pointing at the start of next instruction */ frame->state.ip = ip + 8; - ves_pinvoke_method (imethod, csignature, (MonoFuncV)code, context, frame, (stackval*)(locals + ip [1]), (stackval*)(locals + ip [3]), save_last_error, cache); + ves_pinvoke_method (imethod, csignature, (MonoFuncV)code, context, frame, (stackval*)(locals + ip [1]), (stackval*)(locals + ip [3]), save_last_error, cache, &gc_transitions); - EXCEPTION_CHECKPOINT_GC_UNSAFE; + EXCEPTION_CHECKPOINT; CHECK_RESUME_STATE (context); ip += 8; @@ -6256,8 +6259,8 @@ MINT_IN_CASE(MINT_BRTRUE_I8_SP) ZEROP_SP(gint64, !=); MINT_IN_BREAK; MINT_IN_CASE(MINT_ICALL_PPPPP_V) MINT_IN_CASE(MINT_ICALL_PPPPPP_V) frame->state.ip = ip + 3; - do_icall_wrapper (frame, NULL, *ip, NULL, (stackval*)(locals + ip [1]), frame->imethod->data_items [ip [2]], FALSE); - EXCEPTION_CHECKPOINT_GC_UNSAFE; + do_icall_wrapper (frame, NULL, *ip, NULL, (stackval*)(locals + ip [1]), frame->imethod->data_items [ip [2]], FALSE, &gc_transitions); + EXCEPTION_CHECKPOINT; CHECK_RESUME_STATE (context); ip += 3; MINT_IN_BREAK; @@ -6269,8 +6272,8 @@ MINT_IN_CASE(MINT_BRTRUE_I8_SP) ZEROP_SP(gint64, !=); MINT_IN_BREAK; MINT_IN_CASE(MINT_ICALL_PPPPP_P) MINT_IN_CASE(MINT_ICALL_PPPPPP_P) frame->state.ip = ip + 4; - do_icall_wrapper (frame, NULL, *ip, (stackval*)(locals + ip [1]), (stackval*)(locals + ip [2]), frame->imethod->data_items [ip [3]], FALSE); - EXCEPTION_CHECKPOINT_GC_UNSAFE; + do_icall_wrapper (frame, NULL, *ip, (stackval*)(locals + ip [1]), (stackval*)(locals + ip [2]), frame->imethod->data_items [ip [3]], FALSE, &gc_transitions); + EXCEPTION_CHECKPOINT; CHECK_RESUME_STATE (context); ip += 4; MINT_IN_BREAK; @@ -6320,9 +6323,9 @@ MINT_IN_CASE(MINT_BRTRUE_I8_SP) ZEROP_SP(gint64, !=); MINT_IN_BREAK; LOCAL_VAR (ip [1], gpointer) = mono_domain_get (); ip += 2; MINT_IN_BREAK; - MINT_IN_CASE(MINT_MONO_GET_SP) - LOCAL_VAR (ip [1], gpointer) = frame; - ip += 2; + MINT_IN_CASE(MINT_MONO_ENABLE_GCTRANS) + gc_transitions = TRUE; + ip++; MINT_IN_BREAK; MINT_IN_CASE(MINT_SDB_INTR_LOC) if (G_UNLIKELY (ss_enabled)) { diff --git a/src/mono/mono/mini/interp/mintops.def b/src/mono/mono/mini/interp/mintops.def index a274ff2c1b4ad..3f55869659543 100644 --- a/src/mono/mono/mini/interp/mintops.def +++ b/src/mono/mono/mini/interp/mintops.def @@ -697,7 +697,7 @@ OPDEF(MINT_MONO_ATOMIC_STORE_I4, "mono_atomic.store.i4", 3, 0, 2, MintOpNoArgs) OPDEF(MINT_MONO_MEMORY_BARRIER, "mono_memory_barrier", 1, 0, 0, MintOpNoArgs) OPDEF(MINT_MONO_EXCHANGE_I8, "mono_interlocked.xchg.i8", 4, 1, 2, MintOpNoArgs) OPDEF(MINT_MONO_LDDOMAIN, "mono_lddomain", 2, 1, 0, MintOpNoArgs) -OPDEF(MINT_MONO_GET_SP, "mono_get_sp", 2, 1, 0, MintOpNoArgs) +OPDEF(MINT_MONO_ENABLE_GCTRANS, "mono_enable_gctrans", 1, 0, 0, MintOpNoArgs) OPDEF(MINT_SDB_INTR_LOC, "sdb_intr_loc", 1, 0, 0, MintOpNoArgs) OPDEF(MINT_SDB_SEQ_POINT, "sdb_seq_point", 1, 0, 0, MintOpNoArgs) diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 579245bbbe4c8..979f7a1b7ffe3 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -7127,12 +7127,28 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, save_last_error = TRUE; ++td->ip; break; - case CEE_MONO_GET_SP: - interp_add_ins (td, MINT_MONO_GET_SP); - push_simple_type (td, STACK_TYPE_I); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + case CEE_MONO_GET_SP: { + ++td->ip; + g_assert (*td->ip == MONO_CUSTOM_PREFIX); ++td->ip; + g_assert (*td->ip == CEE_MONO_ICALL); + // in coop gc transitions we use mono.get.sp + calli to implement enter/exit + // on interpreter we do these transitions explicitly when entering/exiting the + // interpreter so we can ignore them here in the wrappers. + MonoJitICallId const jit_icall_id = (MonoJitICallId)read32 (td->ip + 1); + MonoJitICallInfo const * const info = mono_find_jit_icall_info (jit_icall_id); + + if (info->sig->ret->type != MONO_TYPE_VOID) { + // Push a dummy coop gc var + push_simple_type (td, STACK_TYPE_I); + interp_add_ins (td, MINT_MONO_ENABLE_GCTRANS); + } else { + // Pop the unused gc var + td->sp--; + } + td->ip += 5; break; + } default: g_error ("transform.c: Unimplemented opcode: 0xF0 %02x at 0x%x\n", *td->ip, td->ip-header->code); } From 5829730d1671cd96e9997292b5059b122c35c67d Mon Sep 17 00:00:00 2001 From: Manish Godse <61718172+mangod9@users.noreply.github.com> Date: Wed, 13 Oct 2021 06:27:42 -0700 Subject: [PATCH 063/106] Fix GCC build break (#60333) Notice the unescaped '\' within the comments causing a build break on GCC 11 --- src/coreclr/jit/morph.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 2f6f32593a717..1187d4ba193a0 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -10907,6 +10907,7 @@ GenTree* Compiler::fgMorphCastedBitwiseOp(GenTreeOp* tree) return nullptr; } + /* // Reuse gentree nodes: // // tree op1 @@ -10916,6 +10917,7 @@ GenTree* Compiler::fgMorphCastedBitwiseOp(GenTreeOp* tree) // x y x y // // (op2 becomes garbage) + */ tree->gtOp1 = op1->AsCast()->CastOp(); tree->gtOp2 = op2->AsCast()->CastOp(); From c4a15decfa2ab62256a7894cb962e7bcad278414 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Wed, 13 Oct 2021 11:54:25 -0400 Subject: [PATCH 064/106] Implement RSA-PSS signature for CmsSigner Adds the ability to indicate an RSA-based signer wants to use PSS instead of PKCS1. Co-authored-by: Jeremy Barton --- ...m.Security.Cryptography.Pkcs.netcoreapp.cs | 2 + .../src/CompatibilitySuppressions.xml | 20 +- ...ingRefApiCompatBaseline.netstandard2.0.txt | 3 + .../src/Resources/Strings.resx | 3 + .../Cryptography/Pkcs/CmsSignature.DSA.cs | 6 +- .../Cryptography/Pkcs/CmsSignature.ECDsa.cs | 6 +- .../Cryptography/Pkcs/CmsSignature.RSA.cs | 221 ++++++++++++++---- .../Cryptography/Pkcs/CmsSignature.cs | 64 ++++- .../Security/Cryptography/Pkcs/CmsSigner.cs | 72 +++++- .../Security/Cryptography/Pkcs/SignerInfo.cs | 6 +- .../tests/Oids.cs | 1 + .../tests/SignedCms/CmsSignerTests.cs | 39 ++++ .../SignedCms/SignedCmsTests.netcoreapp.cs | 118 ++++++++++ 13 files changed, 506 insertions(+), 55 deletions(-) diff --git a/src/libraries/System.Security.Cryptography.Pkcs/ref/System.Security.Cryptography.Pkcs.netcoreapp.cs b/src/libraries/System.Security.Cryptography.Pkcs/ref/System.Security.Cryptography.Pkcs.netcoreapp.cs index d92fd4fbbd86b..0f53e8d734e9f 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/ref/System.Security.Cryptography.Pkcs.netcoreapp.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/ref/System.Security.Cryptography.Pkcs.netcoreapp.cs @@ -15,7 +15,9 @@ public CmsRecipient(System.Security.Cryptography.Pkcs.SubjectIdentifierType reci public sealed partial class CmsSigner { public CmsSigner(System.Security.Cryptography.Pkcs.SubjectIdentifierType signerIdentifierType, System.Security.Cryptography.X509Certificates.X509Certificate2? certificate, System.Security.Cryptography.AsymmetricAlgorithm? privateKey) { } + public CmsSigner(System.Security.Cryptography.Pkcs.SubjectIdentifierType signerIdentifierType, System.Security.Cryptography.X509Certificates.X509Certificate2? certificate, System.Security.Cryptography.RSA? privateKey, System.Security.Cryptography.RSASignaturePadding? signaturePadding) { } public System.Security.Cryptography.AsymmetricAlgorithm? PrivateKey { get { throw null; } set { } } + public System.Security.Cryptography.RSASignaturePadding? SignaturePadding { get { throw null; } set { } } } public sealed partial class ContentInfo { diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/CompatibilitySuppressions.xml b/src/libraries/System.Security.Cryptography.Pkcs/src/CompatibilitySuppressions.xml index daad0d31168c6..459caaa3038b2 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/CompatibilitySuppressions.xml @@ -1,5 +1,23 @@  + + CP0002 + M:System.Security.Cryptography.Pkcs.CmsSigner.get_SignaturePadding + lib/netstandard2.0/System.Security.Cryptography.Pkcs.dll + lib/net462/System.Security.Cryptography.Pkcs.dll + + + CP0002 + M:System.Security.Cryptography.Pkcs.CmsSigner.set_SignaturePadding(System.Security.Cryptography.RSASignaturePadding) + lib/netstandard2.0/System.Security.Cryptography.Pkcs.dll + lib/net462/System.Security.Cryptography.Pkcs.dll + + + CP0002 + M:System.Security.Cryptography.Pkcs.CmsSigner.#ctor(System.Security.Cryptography.Pkcs.SubjectIdentifierType,System.Security.Cryptography.X509Certificates.X509Certificate2,System.Security.Cryptography.RSA,System.Security.Cryptography.RSASignaturePadding) + lib/netstandard2.0/System.Security.Cryptography.Pkcs.dll + lib/net462/System.Security.Cryptography.Pkcs.dll + CP0002 M:System.Security.Cryptography.Pkcs.CmsSigner.get_PrivateKey @@ -184,4 +202,4 @@ runtimes/win/lib/netstandard2.0/System.Security.Cryptography.Pkcs.dll true - \ No newline at end of file + diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/MatchingRefApiCompatBaseline.netstandard2.0.txt b/src/libraries/System.Security.Cryptography.Pkcs/src/MatchingRefApiCompatBaseline.netstandard2.0.txt index f9e2c1a544038..e644784de848a 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/MatchingRefApiCompatBaseline.netstandard2.0.txt +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/MatchingRefApiCompatBaseline.netstandard2.0.txt @@ -5,8 +5,11 @@ MembersMustExist : Member 'public void System.Security.Cryptography.Pkcs.Algorit MembersMustExist : Member 'public System.Boolean System.Security.Cryptography.Pkcs.CmsRecipientCollection.IsSynchronized.get()' does not exist in the reference but it does exist in the implementation. MembersMustExist : Member 'public System.Object System.Security.Cryptography.Pkcs.CmsRecipientCollection.SyncRoot.get()' does not exist in the reference but it does exist in the implementation. MembersMustExist : Member 'public void System.Security.Cryptography.Pkcs.CmsSigner..ctor(System.Security.Cryptography.Pkcs.SubjectIdentifierType, System.Security.Cryptography.X509Certificates.X509Certificate2, System.Security.Cryptography.AsymmetricAlgorithm)' does not exist in the reference but it does exist in the implementation. +MembersMustExist : Member 'public void System.Security.Cryptography.Pkcs.CmsSigner..ctor(System.Security.Cryptography.Pkcs.SubjectIdentifierType, System.Security.Cryptography.X509Certificates.X509Certificate2, System.Security.Cryptography.RSA, System.Security.Cryptography.RSASignaturePadding)' does not exist in the reference but it does exist in the implementation. MembersMustExist : Member 'public System.Security.Cryptography.AsymmetricAlgorithm System.Security.Cryptography.Pkcs.CmsSigner.PrivateKey.get()' does not exist in the reference but it does exist in the implementation. MembersMustExist : Member 'public void System.Security.Cryptography.Pkcs.CmsSigner.PrivateKey.set(System.Security.Cryptography.AsymmetricAlgorithm)' does not exist in the reference but it does exist in the implementation. +MembersMustExist : Member 'public System.Security.Cryptography.RSASignaturePadding System.Security.Cryptography.Pkcs.CmsSigner.SignaturePadding.get()' does not exist in the reference but it does exist in the implementation. +MembersMustExist : Member 'public void System.Security.Cryptography.Pkcs.CmsSigner.SignaturePadding.set(System.Security.Cryptography.RSASignaturePadding)' does not exist in the reference but it does exist in the implementation. MembersMustExist : Member 'public System.Security.Cryptography.Oid System.Security.Cryptography.Pkcs.ContentInfo.GetContentType(System.ReadOnlySpan)' does not exist in the reference but it does exist in the implementation. MembersMustExist : Member 'public void System.Security.Cryptography.Pkcs.EnvelopedCms.Decode(System.ReadOnlySpan)' does not exist in the reference but it does exist in the implementation. MembersMustExist : Member 'public void System.Security.Cryptography.Pkcs.EnvelopedCms.Decrypt(System.Security.Cryptography.Pkcs.RecipientInfo, System.Security.Cryptography.AsymmetricAlgorithm)' does not exist in the reference but it does exist in the implementation. diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/Resources/Strings.resx b/src/libraries/System.Security.Cryptography.Pkcs/src/Resources/Strings.resx index dad16b2a82a28..0b0701deadb05 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/Resources/Strings.resx +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/Resources/Strings.resx @@ -76,6 +76,9 @@ Value was invalid. + + The RSA padding must be Pkcs1 or Pss. + Index was out of range. Must be non-negative and less than the size of the collection. diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.DSA.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.DSA.cs index d6d6c5b4bafab..49a3b1815e926 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.DSA.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.DSA.cs @@ -29,6 +29,8 @@ private sealed class DSACmsSignature : CmsSignature private readonly HashAlgorithmName _expectedDigest; private readonly string? _signatureAlgorithm; + internal override RSASignaturePadding? SignaturePadding => null; + internal DSACmsSignature(string? signatureAlgorithm, HashAlgorithmName expectedDigest) { _signatureAlgorithm = signatureAlgorithm; @@ -109,9 +111,11 @@ protected override bool Sign( AsymmetricAlgorithm? key, bool silent, [NotNullWhen(true)] out string? signatureAlgorithm, - [NotNullWhen(true)] out byte[]? signatureValue) + [NotNullWhen(true)] out byte[]? signatureValue, + out byte[]? signatureParameters) { Debug.Assert(Helpers.IsDSASupported); + signatureParameters = null; // If there's no private key, fall back to the public key for a "no private key" exception. DSA? dsa = key as DSA ?? diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.ECDsa.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.ECDsa.cs index cac04b086150e..5fc08e0870e9b 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.ECDsa.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.ECDsa.cs @@ -25,6 +25,8 @@ private sealed partial class ECDsaCmsSignature : CmsSignature private readonly HashAlgorithmName _expectedDigest; private readonly string? _signatureAlgorithm; + internal override RSASignaturePadding? SignaturePadding => null; + internal ECDsaCmsSignature(string? signatureAlgorithm, HashAlgorithmName expectedDigest) { _signatureAlgorithm = signatureAlgorithm; @@ -108,8 +110,10 @@ protected override bool Sign( AsymmetricAlgorithm? certKey, bool silent, [NotNullWhen(true)] out string? signatureAlgorithm, - [NotNullWhen(true)] out byte[]? signatureValue) + [NotNullWhen(true)] out byte[]? signatureValue, + out byte[]? signatureParameters) { + signatureParameters = null; // If there's no private key, fall back to the public key for a "no private key" exception. ECDsa? key = certKey as ECDsa ?? PkcsPal.Instance.GetPrivateKeyForSigning(certificate, silent) ?? diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.RSA.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.RSA.cs index 7873de050dd81..2f26c108e8087 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.RSA.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.RSA.cs @@ -90,39 +90,8 @@ protected abstract RSASignaturePadding GetSignaturePadding( string? digestAlgorithmOid, HashAlgorithmName digestAlgorithmName, int digestValueLength); - } - - private sealed class RSAPkcs1CmsSignature : RSACmsSignature - { - public RSAPkcs1CmsSignature(string? signatureAlgorithm, HashAlgorithmName? expectedDigest) - : base(signatureAlgorithm, expectedDigest) - { - } - - protected override RSASignaturePadding GetSignaturePadding( - ReadOnlyMemory? signatureParameters, - string? digestAlgorithmOid, - HashAlgorithmName digestAlgorithmName, - int digestValueLength) - { - if (signatureParameters == null) - { - return RSASignaturePadding.Pkcs1; - } - - Span expectedParameters = stackalloc byte[2]; - expectedParameters[0] = 0x05; - expectedParameters[1] = 0x00; - - if (expectedParameters.SequenceEqual(signatureParameters.Value.Span)) - { - return RSASignaturePadding.Pkcs1; - } - throw new CryptographicException(SR.Cryptography_Pkcs_InvalidSignatureParameters); - } - - protected override bool Sign( + private protected bool SignCore( #if NETCOREAPP || NETSTANDARD2_1 ReadOnlySpan dataHash, #else @@ -132,7 +101,7 @@ protected override bool Sign( X509Certificate2 certificate, AsymmetricAlgorithm? key, bool silent, - [NotNullWhen(true)] out string? signatureAlgorithm, + RSASignaturePadding signaturePadding, [NotNullWhen(true)] out byte[]? signatureValue) { RSA certPublicKey = certificate.GetRSAPublicKey()!; @@ -142,15 +111,12 @@ protected override bool Sign( PkcsPal.Instance.GetPrivateKeyForSigning(certificate, silent) ?? certPublicKey; - if (privateKey == null) + if (privateKey is null) { - signatureAlgorithm = null; signatureValue = null; return false; } - signatureAlgorithm = Oids.Rsa; - #if NETCOREAPP || NETSTANDARD2_1 byte[] signature = new byte[privateKey.KeySize / 8]; @@ -158,14 +124,14 @@ protected override bool Sign( dataHash, signature, hashAlgorithmName, - RSASignaturePadding.Pkcs1, + signaturePadding, out int bytesWritten); if (signed && signature.Length == bytesWritten) { signatureValue = signature; - if (key != null && !certPublicKey.VerifyHash(dataHash, signatureValue, hashAlgorithmName, RSASignaturePadding.Pkcs1)) + if (key is not null && !certPublicKey.VerifyHash(dataHash, signatureValue, hashAlgorithmName, signaturePadding)) { // key did not match certificate signatureValue = null; @@ -182,9 +148,9 @@ protected override bool Sign( dataHash, #endif hashAlgorithmName, - RSASignaturePadding.Pkcs1); + signaturePadding); - if (key != null && !certPublicKey.VerifyHash(dataHash, signatureValue, hashAlgorithmName, RSASignaturePadding.Pkcs1)) + if (key is not null && !certPublicKey.VerifyHash(dataHash, signatureValue, hashAlgorithmName, signaturePadding)) { // key did not match certificate signatureValue = null; @@ -195,8 +161,128 @@ protected override bool Sign( } } + private sealed class RSAPkcs1CmsSignature : RSACmsSignature + { + internal override RSASignaturePadding? SignaturePadding => RSASignaturePadding.Pkcs1; + + public RSAPkcs1CmsSignature(string? signatureAlgorithm, HashAlgorithmName? expectedDigest) + : base(signatureAlgorithm, expectedDigest) + { + } + + protected override RSASignaturePadding GetSignaturePadding( + ReadOnlyMemory? signatureParameters, + string? digestAlgorithmOid, + HashAlgorithmName digestAlgorithmName, + int digestValueLength) + { + if (signatureParameters == null) + { + return RSASignaturePadding.Pkcs1; + } + + Span expectedParameters = stackalloc byte[2]; + expectedParameters[0] = 0x05; + expectedParameters[1] = 0x00; + + if (expectedParameters.SequenceEqual(signatureParameters.Value.Span)) + { + return RSASignaturePadding.Pkcs1; + } + + throw new CryptographicException(SR.Cryptography_Pkcs_InvalidSignatureParameters); + } + + protected override bool Sign( +#if NETCOREAPP || NETSTANDARD2_1 + ReadOnlySpan dataHash, +#else + byte[] dataHash, +#endif + HashAlgorithmName hashAlgorithmName, + X509Certificate2 certificate, + AsymmetricAlgorithm? key, + bool silent, + [NotNullWhen(true)] out string? signatureAlgorithm, + [NotNullWhen(true)] out byte[]? signatureValue, + out byte[]? signatureParameters) + { + bool result = SignCore( + dataHash, + hashAlgorithmName, + certificate, + key, + silent, + RSASignaturePadding.Pkcs1, + out signatureValue); + + signatureAlgorithm = result ? Oids.Rsa : null; + signatureParameters = null; + return result; + } + } + private sealed class RSAPssCmsSignature : RSACmsSignature { + // SEQUENCE + private static readonly byte[] s_rsaPssSha1Parameters = new byte[] { 0x30, 0x00 }; + + // SEQUENCE + // [0] + // SEQUENCE + // OBJECT IDENTIFIER 2.16.840.1.101.3.4.2.1 + // [1] + // SEQUENCE + // OBJECT IDENTIFIER 1.2.840.113549.1.1.8 + // SEQUENCE + // OBJECT IDENTIFIER 2.16.840.1.101.3.4.2.1 + // [2] + // INTEGER 32 + private static readonly byte[] s_rsaPssSha256Parameters = new byte[] { + 0x30, 0x30, 0xA0, 0x0D, 0x30, 0x0B, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, + 0x01, 0xA1, 0x1A, 0x30, 0x18, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x08, + 0x30, 0x0B, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0xA2, 0x03, 0x02, + 0x01, 0x20, + }; + + // SEQUENCE + // [0] + // SEQUENCE + // OBJECT IDENTIFIER 2.16.840.1.101.3.4.2.2 + // [1] + // SEQUENCE + // OBJECT IDENTIFIER 1.2.840.113549.1.1.8 + // SEQUENCE + // OBJECT IDENTIFIER 2.16.840.1.101.3.4.2.2 + // [2] + // INTEGER 48 + private static readonly byte[] s_rsaPssSha384Parameters = new byte[] { + 0x30, 0x30, 0xA0, 0x0D, 0x30, 0x0B, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, + 0x02, 0xA1, 0x1A, 0x30, 0x18, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x08, + 0x30, 0x0B, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0xA2, 0x03, 0x02, + 0x01, 0x30, + }; + + // SEQUENCE + // [0] + // SEQUENCE + // OBJECT IDENTIFIER 2.16.840.1.101.3.4.2.3 + // [1] + // SEQUENCE + // OBJECT IDENTIFIER 1.2.840.113549.1.1.8 + // SEQUENCE + // OBJECT IDENTIFIER 2.16.840.1.101.3.4.2.3 + // [2] + // INTEGER 64 + private static readonly byte[] s_rsaPssSha512Parameters = new byte[] { + 0x30, 0x30, 0xA0, 0x0D, 0x30, 0x0B, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, + 0x03, 0xA1, 0x1A, 0x30, 0x18, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x08, + 0x30, 0x0B, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0xA2, 0x03, 0x02, + 0x01, 0x40, + }; + + internal override RSASignaturePadding? SignaturePadding => RSASignaturePadding.Pss; + public RSAPssCmsSignature() : base(null, null) { } @@ -276,11 +362,56 @@ protected override bool Sign( X509Certificate2 certificate, AsymmetricAlgorithm? key, bool silent, - out string signatureAlgorithm, - out byte[] signatureValue) + [NotNullWhen(true)] out string? signatureAlgorithm, + [NotNullWhen(true)] out byte[]? signatureValue, + out byte[]? signatureParameters) { - Debug.Fail("RSA-PSS requires building parameters, which has no API."); - throw new CryptographicException(); + bool result = SignCore( + dataHash, + hashAlgorithmName, + certificate, + key, + silent, + RSASignaturePadding.Pss, + out signatureValue); + + if (result) + { + signatureAlgorithm = Oids.RsaPss; + + if (hashAlgorithmName == HashAlgorithmName.SHA1) + { + signatureParameters = s_rsaPssSha1Parameters; + } + else if (hashAlgorithmName == HashAlgorithmName.SHA256) + { + signatureParameters = s_rsaPssSha256Parameters; + } + else if (hashAlgorithmName == HashAlgorithmName.SHA384) + { + signatureParameters = s_rsaPssSha384Parameters; + } + else if (hashAlgorithmName == HashAlgorithmName.SHA512) + { + signatureParameters = s_rsaPssSha512Parameters; + } + else + { + // The only hash algorithm we don't support is MD5. + // We shouldn't get here with anything other than MD5. + Debug.Assert(hashAlgorithmName == HashAlgorithmName.MD5, $"Unsupported digest algorithm '{hashAlgorithmName.Name}'"); + signatureAlgorithm = null; + signatureParameters = null; + return false; + } + } + else + { + signatureAlgorithm = null; + signatureParameters = null; + } + + return result; } } } diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.cs index bb6680465d93d..7cf3436b54aa6 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.cs @@ -26,6 +26,7 @@ static CmsSignature() static partial void PrepareRegistrationDsa(Dictionary lookup); static partial void PrepareRegistrationECDsa(Dictionary lookup); + internal abstract RSASignaturePadding? SignaturePadding { get; } protected abstract bool VerifyKeyType(AsymmetricAlgorithm key); internal abstract bool VerifySignature( @@ -52,12 +53,53 @@ protected abstract bool Sign( AsymmetricAlgorithm? key, bool silent, [NotNullWhen(true)] out string? signatureAlgorithm, - [NotNullWhen(true)] out byte[]? signatureValue); + [NotNullWhen(true)] out byte[]? signatureValue, + out byte[]? signatureParameters); - internal static CmsSignature? ResolveAndVerifyKeyType(string signatureAlgorithmOid, AsymmetricAlgorithm? key) + internal static CmsSignature? ResolveAndVerifyKeyType( + string signatureAlgorithmOid, + AsymmetricAlgorithm? key, + RSASignaturePadding? rsaSignaturePadding) { + // Rules: + // RSASignaturePadding 'wins' if specified if the signatureAlgorithmOid is any RSA OID. + // if there is no rsaSignaturePadding, the OID is used. + // If the rsaSignaturePadding is specified and the signatureAlgorithm OID is not any + // RSA OID, this is invalid, so null. if (s_lookup.TryGetValue(signatureAlgorithmOid, out CmsSignature? processor)) { + // We have a padding that might override the OID. + if (rsaSignaturePadding is not null) + { + // The processor does not support RSA signature padding + if (processor.SignaturePadding is null) + { + // We were given an RSA signature padding, but the processor is not any known RSA. + // We won't override a non-RSA OID (like ECDSA) to RSA based on the padding, so return null. + return null; + } + + // The processor is RSA, but does not agree with the specified signature padding, so override. + if (processor.SignaturePadding != rsaSignaturePadding) + { + if (rsaSignaturePadding == RSASignaturePadding.Pkcs1) + { + processor = s_lookup[Oids.Rsa]; + Debug.Assert(processor is not null); + } + else if (rsaSignaturePadding == RSASignaturePadding.Pss) + { + processor = s_lookup[Oids.RsaPss]; + Debug.Assert(processor is not null); + } + else + { + Debug.Fail("Unhandled RSA signature padding."); + return null; + } + } + } + if (key != null && !processor.VerifyKeyType(key)) { return null; @@ -79,21 +121,33 @@ internal static bool Sign( X509Certificate2 certificate, AsymmetricAlgorithm? key, bool silent, + RSASignaturePadding? rsaSignaturePadding, out string? oid, - out ReadOnlyMemory signatureValue) + out ReadOnlyMemory signatureValue, + out ReadOnlyMemory signatureParameters) { - CmsSignature? processor = ResolveAndVerifyKeyType(certificate.GetKeyAlgorithm(), key); + CmsSignature? processor = ResolveAndVerifyKeyType(certificate.GetKeyAlgorithm(), key, rsaSignaturePadding); if (processor == null) { oid = null; signatureValue = default; + signatureParameters = default; return false; } - bool signed = processor.Sign(dataHash, hashAlgorithmName, certificate, key, silent, out oid, out byte[]? signature); + bool signed = processor.Sign( + dataHash, + hashAlgorithmName, + certificate, + key, + silent, + out oid, + out byte[]? signature, + out byte[]? parameters); signatureValue = signature; + signatureParameters = parameters; return signed; } diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSigner.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSigner.cs index cd94142c6d0cf..8b9a401a81b36 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSigner.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSigner.cs @@ -17,6 +17,7 @@ public sealed class CmsSigner private static readonly Oid s_defaultAlgorithm = Oids.Sha256Oid; private SubjectIdentifierType _signerIdentifierType; + private RSASignaturePadding? _signaturePadding; public X509Certificate2? Certificate { get; set; } public AsymmetricAlgorithm? PrivateKey { get; set; } @@ -26,6 +27,25 @@ public sealed class CmsSigner public CryptographicAttributeObjectCollection SignedAttributes { get; private set; } = new CryptographicAttributeObjectCollection(); public CryptographicAttributeObjectCollection UnsignedAttributes { get; private set; } = new CryptographicAttributeObjectCollection(); + /// + /// Gets or sets the RSA signature padding to use. + /// + /// The RSA signature padding to use. + public RSASignaturePadding? SignaturePadding + { + get => _signaturePadding; + set + { + if (value is not null && + value != RSASignaturePadding.Pkcs1 && value != RSASignaturePadding.Pss) + { + throw new ArgumentException(SR.Argument_InvalidRsaSignaturePadding, nameof(value)); + } + + _signaturePadding = value; + } + } + public SubjectIdentifierType SignerIdentifierType { get { return _signerIdentifierType; } @@ -62,7 +82,48 @@ public CmsSigner(SubjectIdentifierType signerIdentifierType, X509Certificate2? c } public CmsSigner(SubjectIdentifierType signerIdentifierType, X509Certificate2? certificate, AsymmetricAlgorithm? privateKey) + : this(signerIdentifierType, certificate, privateKey, signaturePadding: null) { + } + + /// + /// Initializes a new instance of the CmsSigner class with a specified signer + /// certificate, subject identifier type, private key object, and RSA signature padding. + /// + /// + /// One of the enumeration values that specifies the scheme to use for identifying + /// which signing certificate was used. + /// + /// + /// The certificate whose private key will be used to sign a message. + /// + /// + /// The private key object to use when signing the message. + /// + /// + /// The RSA signature padding to use. + /// + public CmsSigner( + SubjectIdentifierType signerIdentifierType, + X509Certificate2? certificate, + RSA? privateKey, + RSASignaturePadding? signaturePadding) + : this(signerIdentifierType, certificate, (AsymmetricAlgorithm?)privateKey, signaturePadding) + { + } + + private CmsSigner( + SubjectIdentifierType signerIdentifierType, + X509Certificate2? certificate, + AsymmetricAlgorithm? privateKey, + RSASignaturePadding? signaturePadding) + { + if (signaturePadding is not null && + signaturePadding != RSASignaturePadding.Pkcs1 && signaturePadding != RSASignaturePadding.Pss) + { + throw new ArgumentException(SR.Argument_InvalidRsaSignaturePadding, nameof(signaturePadding)); + } + switch (signerIdentifierType) { case SubjectIdentifierType.Unknown: @@ -90,6 +151,7 @@ public CmsSigner(SubjectIdentifierType signerIdentifierType, X509Certificate2? c Certificate = certificate; DigestAlgorithm = s_defaultAlgorithm.CopyOid(); PrivateKey = privateKey; + _signaturePadding = signaturePadding; } internal void CheckCertificateValue() @@ -212,6 +274,7 @@ internal SignerInfoAsn Sign( bool signed; string? signatureAlgorithm; ReadOnlyMemory signatureValue; + ReadOnlyMemory signatureParameters = default; if (SignerIdentifierType == SubjectIdentifierType.NoSignature) { @@ -227,8 +290,10 @@ internal SignerInfoAsn Sign( Certificate!, PrivateKey, silent, + SignaturePadding, out signatureAlgorithm, - out signatureValue); + out signatureValue, + out signatureParameters); } if (!signed) @@ -239,6 +304,11 @@ internal SignerInfoAsn Sign( newSignerInfo.SignatureValue = signatureValue; newSignerInfo.SignatureAlgorithm.Algorithm = signatureAlgorithm!; + if (!signatureParameters.IsEmpty) + { + newSignerInfo.SignatureAlgorithm.Parameters = signatureParameters; + } + X509Certificate2Collection certs = new X509Certificate2Collection(); certs.AddRange(Certificates); diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SignerInfo.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SignerInfo.cs index af5714360543a..9c7874b451c42 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SignerInfo.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SignerInfo.cs @@ -690,7 +690,11 @@ private void Verify( X509Certificate2 certificate, bool verifySignatureOnly) { - CmsSignature? signatureProcessor = CmsSignature.ResolveAndVerifyKeyType(SignatureAlgorithm.Value!, key: null); + // SignatureAlgorithm always 'wins' so we don't need to pass in an rsaSignaturePadding + CmsSignature? signatureProcessor = CmsSignature.ResolveAndVerifyKeyType( + SignatureAlgorithm.Value!, + key: null, + rsaSignaturePadding: null); if (signatureProcessor == null) { diff --git a/src/libraries/System.Security.Cryptography.Pkcs/tests/Oids.cs b/src/libraries/System.Security.Cryptography.Pkcs/tests/Oids.cs index 0fae7af2c5822..eded07b0913eb 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/tests/Oids.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/tests/Oids.cs @@ -22,6 +22,7 @@ internal static class Oids public const string RsaPkcs1Sha256 = "1.2.840.113549.1.1.11"; public const string RsaPkcs1Sha384 = "1.2.840.113549.1.1.12"; public const string RsaPkcs1Sha512 = "1.2.840.113549.1.1.13"; + public const string RsaPss = "1.2.840.113549.1.1.10"; public const string Esdh = "1.2.840.113549.1.9.16.3.5"; public const string Dh = "1.2.840.10046.2.1"; public const string EcdsaSha256 = "1.2.840.10045.4.3.2"; diff --git a/src/libraries/System.Security.Cryptography.Pkcs/tests/SignedCms/CmsSignerTests.cs b/src/libraries/System.Security.Cryptography.Pkcs/tests/SignedCms/CmsSignerTests.cs index 4c73ecc54f354..a99d2f8d4dde9 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/tests/SignedCms/CmsSignerTests.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/tests/SignedCms/CmsSignerTests.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Reflection; using Xunit; namespace System.Security.Cryptography.Pkcs.Tests @@ -19,5 +20,43 @@ public static void SignerIdentifierType_InvalidValues(SubjectIdentifierType inva expectedParamName: null, () => signer.SignerIdentifierType = invalidType); } + +#if NETCOREAPP + [Fact] + public static void SignaturePadding_InvalidValue() + { + RSASignaturePaddingMode badMode = (RSASignaturePaddingMode)(-1); + + // Currently we support all RSASignaturePaddings. However we want to make sure we fail properly + // if an unsupported one is added later, so construct a bogus padding. + RSASignaturePadding badPadding = (RSASignaturePadding)typeof(RSASignaturePadding) + .GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, new Type[] { typeof(RSASignaturePaddingMode)}) + .Invoke(new object[] { badMode }); + + // Test setter + CmsSigner signer = new CmsSigner(); + AssertExtensions.Throws("value", () => signer.SignaturePadding = badPadding); + + // Test ctor + AssertExtensions.Throws("signaturePadding", () => new CmsSigner( + SubjectIdentifierType.IssuerAndSerialNumber, + certificate: null, + privateKey: null, + badPadding)); + } + + [Fact] + public static void SignaturePadding_Null() + { + CmsSigner signer = new CmsSigner(); + signer.SignaturePadding = null; // Assert.NoThrow + + _ = new CmsSigner( + SubjectIdentifierType.IssuerAndSerialNumber, + certificate: null, + privateKey: null, + signaturePadding: null); // Assert.NoThrow + } +#endif } } diff --git a/src/libraries/System.Security.Cryptography.Pkcs/tests/SignedCms/SignedCmsTests.netcoreapp.cs b/src/libraries/System.Security.Cryptography.Pkcs/tests/SignedCms/SignedCmsTests.netcoreapp.cs index cfa17458204e5..de363e3873897 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/tests/SignedCms/SignedCmsTests.netcoreapp.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/tests/SignedCms/SignedCmsTests.netcoreapp.cs @@ -479,6 +479,124 @@ public static void CreateSignature_DigestAlgorithmWithSignatureOid_Prohibited() } } + [Theory] + [InlineData(Oids.Sha256, true)] + [InlineData(Oids.Sha384, true)] + [InlineData(Oids.Sha512, true)] + [InlineData(Oids.Sha1, true)] + [InlineData(Oids.Sha256, false)] + [InlineData(Oids.Sha384, false)] + [InlineData(Oids.Sha512, false)] + [InlineData(Oids.Sha1, false)] + public static void CreateSignature_RsaPss(string digestOid, bool assignByConstructor) + { + ContentInfo content = new ContentInfo(new byte[] { 1, 2, 3 }); + SignedCms cms = new SignedCms(content); + byte[] cmsBytes; + + using (X509Certificate2 cert = Certificates.RSA2048SignatureOnly.TryGetCertificateWithPrivateKey()) + { + CmsSigner signer; + + if (assignByConstructor) + { + signer = new CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, cert, null, RSASignaturePadding.Pss); + } + else + { + signer = new CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, cert); + signer.SignaturePadding = RSASignaturePadding.Pss; + } + + signer.DigestAlgorithm = new Oid(digestOid, null); + cms.ComputeSignature(signer); + cmsBytes = cms.Encode(); + } + + cms = new SignedCms(); + cms.Decode(cmsBytes); + cms.CheckSignature(true); // Assert.NoThrow + Assert.Single(cms.SignerInfos); + + SignerInfo signerInfo = cms.SignerInfos[0]; + Assert.Equal(Oids.RsaPss, signerInfo.SignatureAlgorithm.Value); + } + + [Fact] + public static void CreateSignature_RsaPss_SeparateKey() + { + ContentInfo content = new ContentInfo(new byte[] { 1, 2, 3 }); + SignedCms cms = new SignedCms(content); + byte[] cmsBytes; + + using (X509Certificate2 cert = Certificates.RSA2048SignatureOnly.TryGetCertificateWithPrivateKey()) + using (X509Certificate2 pubOnly = new X509Certificate2(cert.RawDataMemory.Span)) + using (RSA rsa = cert.GetRSAPrivateKey()) + { + CmsSigner signer = new CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, pubOnly, rsa, RSASignaturePadding.Pss); + cms.ComputeSignature(signer); + cmsBytes = cms.Encode(); + } + + cms = new SignedCms(); + cms.Decode(cmsBytes); + cms.CheckSignature(true); // Assert.NoThrow + Assert.Single(cms.SignerInfos); + + SignerInfo signerInfo = cms.SignerInfos[0]; + Assert.Equal(Oids.RsaPss, signerInfo.SignatureAlgorithm.Value); + } + + [Fact] + public static void CreateSignature_RsaPss_Md5NotSupported() + { + ContentInfo content = new ContentInfo(new byte[] { 1, 2, 3 }); + SignedCms cms = new SignedCms(content); + + using (X509Certificate2 cert = Certificates.RSA2048SignatureOnly.TryGetCertificateWithPrivateKey()) + { + CmsSigner signer = new CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, cert, null, RSASignaturePadding.Pss); + signer.DigestAlgorithm = new Oid(Oids.Md5, null); + Assert.ThrowsAny(() => cms.ComputeSignature(signer)); + } + } + + [Fact] + public static void CreateSignature_RsaPadding_DefaultToPkcs1() + { + ContentInfo content = new ContentInfo(new byte[] { 1, 2, 3 }); + SignedCms cms = new SignedCms(content); + byte[] cmsBytes; + + using (X509Certificate2 cert = Certificates.RSA2048SignatureOnly.TryGetCertificateWithPrivateKey()) + { + CmsSigner signer = new CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, cert, null, null); + cms.ComputeSignature(signer); + cmsBytes = cms.Encode(); + } + + cms = new SignedCms(); + cms.Decode(cmsBytes); + cms.CheckSignature(true); // Assert.NoThrow + Assert.Single(cms.SignerInfos); + + SignerInfo signerInfo = cms.SignerInfos[0]; + Assert.Equal(Oids.Rsa, signerInfo.SignatureAlgorithm.Value); + } + + [Fact] + public static void CreateSignature_Ecdsa_ThrowsWithRsaSignaturePadding() + { + ContentInfo content = new ContentInfo(new byte[] { 1, 2, 3 }); + SignedCms cms = new SignedCms(content); + + using (X509Certificate2 cert = Certificates.ECDsaP256Win.TryGetCertificateWithPrivateKey()) + { + CmsSigner signer = new CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, cert, null, RSASignaturePadding.Pss); + Assert.ThrowsAny(() => cms.ComputeSignature(signer)); + } + } + private static void VerifyWithExplicitPrivateKey(X509Certificate2 cert, AsymmetricAlgorithm key) { using (var pubCert = new X509Certificate2(cert.RawData)) From 2265a61565e20fc26ca8ec0632eb14268b99158e Mon Sep 17 00:00:00 2001 From: Adeel Mujahid <3840695+am11@users.noreply.github.com> Date: Wed, 13 Oct 2021 19:25:12 +0300 Subject: [PATCH 065/106] Fix build with clang 13 (#60328) * Fix build with clang 13 Tested with: ```sh $ clang --version | head -1 Ubuntu clang version 13.0.0-++20211006103153+fd1d8c2f04dd-1~exp1~20211006223759.3 ``` * Fix gcc build * Unify supressions * Fix Clang 13 -Wcast-function-type warning `cast from 'void (*)(int, siginfo_t *, void *)' to 'void (*)(int)' converts to incompatible function type` But going through an intermediate `void (*) (void)` function type is allowed. Co-authored-by: Aleksey Kliger --- eng/native/configurecompiler.cmake | 7 ++++--- src/coreclr/jit/morph.cpp | 2 +- .../Native/Unix/System.Native/pal_process.c | 20 ++++++++++++++++++- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/eng/native/configurecompiler.cmake b/eng/native/configurecompiler.cmake index 3e819071f6111..a29857451bc79 100644 --- a/eng/native/configurecompiler.cmake +++ b/eng/native/configurecompiler.cmake @@ -367,6 +367,8 @@ if (CLR_CMAKE_HOST_UNIX) #These seem to indicate real issues add_compile_options($<$:-Wno-invalid-offsetof>) + add_compile_options(-Wno-unused-but-set-variable) + if (CMAKE_C_COMPILER_ID MATCHES "Clang") add_compile_options(-Wno-unknown-warning-option) @@ -376,8 +378,6 @@ if (CLR_CMAKE_HOST_UNIX) # Disabled warnings add_compile_options(-Wno-unused-private-field) - # Explicit constructor calls are not supported by clang (this->ClassName::ClassName()) - add_compile_options(-Wno-microsoft) # There are constants of type BOOL used in a condition. But BOOL is defined as int # and so the compiler thinks that there is a mistake. add_compile_options(-Wno-constant-logical-operand) @@ -390,8 +390,9 @@ if (CLR_CMAKE_HOST_UNIX) # to a struct or a class that has virtual members or a base class. In that case, clang # may not generate the same object layout as MSVC. add_compile_options(-Wno-incompatible-ms-struct) + + add_compile_options(-Wno-reserved-identifier) else() - add_compile_options(-Wno-unused-but-set-variable) add_compile_options(-Wno-uninitialized) add_compile_options(-Wno-strict-aliasing) add_compile_options(-Wno-array-bounds) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 1187d4ba193a0..aba1b5949f391 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -10913,7 +10913,7 @@ GenTree* Compiler::fgMorphCastedBitwiseOp(GenTreeOp* tree) // tree op1 // / \ | // op1 op2 ==> tree - // | | / \ + // | | / \. // x y x y // // (op2 becomes garbage) diff --git a/src/libraries/Native/Unix/System.Native/pal_process.c b/src/libraries/Native/Unix/System.Native/pal_process.c index 5e97d958f74d1..fabdfae76187c 100644 --- a/src/libraries/Native/Unix/System.Native/pal_process.c +++ b/src/libraries/Native/Unix/System.Native/pal_process.c @@ -191,6 +191,24 @@ static int SetGroups(uint32_t* userGroups, int32_t userGroupsLength, uint32_t* p return rv; } +typedef void (*VoidIntFn)(int); + +static +VoidIntFn +handler_from_sigaction (struct sigaction *sa) +{ + if (((unsigned int)sa->sa_flags) & SA_SIGINFO) + { + // work around -Wcast-function-type + void (*tmp)(void) = (void (*)(void))sa->sa_sigaction; + return (void (*)(int))tmp; + } + else + { + return sa->sa_handler; + } +} + int32_t SystemNative_ForkAndExecProcess(const char* filename, char* const argv[], char* const envp[], @@ -371,7 +389,7 @@ int32_t SystemNative_ForkAndExecProcess(const char* filename, } if (!sigaction(sig, NULL, &sa_old)) { - void (*oldhandler)(int) = (((unsigned int)sa_old.sa_flags) & SA_SIGINFO) ? (void (*)(int))sa_old.sa_sigaction : sa_old.sa_handler; + void (*oldhandler)(int) = handler_from_sigaction (&sa_old); if (oldhandler != SIG_IGN && oldhandler != SIG_DFL) { // It has a custom handler, put the default handler back. From 1a214cab8183794e2ce22fedc700487e579053f6 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Wed, 13 Oct 2021 18:26:09 +0200 Subject: [PATCH 066/106] [main] Update dependencies from 9 repositories (#60216) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update dependencies from https://dev.azure.com/dnceng/internal/_git/dotnet-optimization build 20211008.6 optimization.linux-x64.MIBC.Runtime , optimization.windows_nt-x64.MIBC.Runtime , optimization.windows_nt-x86.MIBC.Runtime , optimization.PGO.CoreCLR From Version 1.0.0-prerelease.21507.4 -> To Version 1.0.0-prerelease.21508.6 * Update dependencies from https://github.com/dotnet/arcade build 20211008.1 Microsoft.DotNet.XUnitExtensions , Microsoft.DotNet.VersionTools.Tasks , Microsoft.DotNet.Build.Tasks.Workloads , Microsoft.DotNet.Build.Tasks.Templating , Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk , Microsoft.DotNet.Build.Tasks.Packaging , Microsoft.DotNet.Build.Tasks.Installers , Microsoft.DotNet.Build.Tasks.Feed , Microsoft.DotNet.Build.Tasks.Archives , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.ApiCompat , Microsoft.DotNet.CodeAnalysis , Microsoft.DotNet.XUnitConsoleRunner , Microsoft.DotNet.GenFacades , Microsoft.DotNet.GenAPI , Microsoft.DotNet.RemoteExecutor , Microsoft.DotNet.PackageTesting , Microsoft.DotNet.Helix.Sdk , Microsoft.DotNet.SharedFramework.Sdk From Version 7.0.0-beta.21507.3 -> To Version 7.0.0-beta.21508.1 * Update dependencies from https://github.com/dotnet/msquic build 20211008.1 System.Net.MsQuic.Transport From Version 6.0.0-preview.7.21480.1 -> To Version 6.0.0-preview.7.21508.1 * Update dependencies from https://dev.azure.com/dnceng/internal/_git/dotnet-optimization build 20211009.6 optimization.linux-x64.MIBC.Runtime , optimization.windows_nt-x64.MIBC.Runtime , optimization.windows_nt-x86.MIBC.Runtime , optimization.PGO.CoreCLR From Version 1.0.0-prerelease.21507.4 -> To Version 1.0.0-prerelease.21509.6 * Update dependencies from https://github.com/dotnet/runtime build 20211011.2 Microsoft.NETCore.ILAsm , Microsoft.NETCore.DotNetHostPolicy , Microsoft.NETCore.DotNetHost , Microsoft.NETCore.App.Runtime.win-x64 , System.Runtime.CompilerServices.Unsafe , runtime.native.System.IO.Ports , Microsoft.NET.Sdk.IL , System.Text.Json From Version 7.0.0-alpha.1.21501.7 -> To Version 7.0.0-alpha.1.21511.2 * Update dependencies from https://dev.azure.com/dnceng/internal/_git/dotnet-optimization build 20211011.2 optimization.linux-x64.MIBC.Runtime , optimization.windows_nt-x64.MIBC.Runtime , optimization.windows_nt-x86.MIBC.Runtime , optimization.PGO.CoreCLR From Version 1.0.0-prerelease.21507.4 -> To Version 1.0.0-prerelease.21511.2 * Update dependencies from https://github.com/dotnet/arcade build 20211011.1 Microsoft.DotNet.XUnitExtensions , Microsoft.DotNet.VersionTools.Tasks , Microsoft.DotNet.Build.Tasks.Workloads , Microsoft.DotNet.Build.Tasks.Templating , Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk , Microsoft.DotNet.Build.Tasks.Packaging , Microsoft.DotNet.Build.Tasks.Installers , Microsoft.DotNet.Build.Tasks.Feed , Microsoft.DotNet.Build.Tasks.Archives , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.ApiCompat , Microsoft.DotNet.CodeAnalysis , Microsoft.DotNet.XUnitConsoleRunner , Microsoft.DotNet.GenFacades , Microsoft.DotNet.GenAPI , Microsoft.DotNet.RemoteExecutor , Microsoft.DotNet.PackageTesting , Microsoft.DotNet.Helix.Sdk , Microsoft.DotNet.SharedFramework.Sdk From Version 7.0.0-beta.21507.3 -> To Version 7.0.0-beta.21511.1 * Update dependencies from https://github.com/dotnet/icu build 20211011.1 Microsoft.NETCore.Runtime.ICU.Transport From Version 7.0.0-alpha.1.21477.1 -> To Version 7.0.0-alpha.1.21511.1 * Update dependencies from https://github.com/dotnet/xharness build 20211011.3 Microsoft.DotNet.XHarness.CLI , Microsoft.DotNet.XHarness.TestRunners.Xunit From Version 1.0.0-prerelease.21508.1 -> To Version 1.0.0-prerelease.21511.3 * Update dependencies from https://github.com/dotnet/runtime-assets build 20211011.2 Microsoft.DotNet.CilStrip.Sources , System.ComponentModel.TypeConverter.TestData , System.Drawing.Common.TestData , System.IO.Compression.TestData , System.IO.Packaging.TestData , System.Net.TestData , System.Private.Runtime.UnicodeData , System.Runtime.Numerics.TestData , System.Runtime.TimeZoneData , System.Security.Cryptography.X509Certificates.TestData , System.Windows.Extensions.TestData From Version 7.0.0-beta.21506.1 -> To Version 7.0.0-beta.21511.2 * Update dependencies from https://github.com/dotnet/msquic build 20211011.1 System.Net.MsQuic.Transport From Version 6.0.0-preview.7.21480.1 -> To Version 6.0.0-preview.7.21511.1 * Update dependencies from https://github.com/dotnet/llvm-project build 20211011.1 runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools , runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk , runtime.osx.10.12-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools , runtime.osx.10.12-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk , runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk , runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools , runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk , runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools From Version 11.1.0-alpha.1.21477.1 -> To Version 11.1.0-alpha.1.21511.1 * Update dependencies from https://dev.azure.com/dnceng/internal/_git/dotnet-optimization build 20211012.6 optimization.linux-x64.MIBC.Runtime , optimization.windows_nt-x64.MIBC.Runtime , optimization.windows_nt-x86.MIBC.Runtime , optimization.PGO.CoreCLR From Version 1.0.0-prerelease.21507.4 -> To Version 1.0.0-prerelease.21512.6 * Update dependencies from https://github.com/dotnet/arcade build 20211012.3 Microsoft.DotNet.XUnitExtensions , Microsoft.DotNet.VersionTools.Tasks , Microsoft.DotNet.Build.Tasks.Workloads , Microsoft.DotNet.Build.Tasks.Templating , Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk , Microsoft.DotNet.Build.Tasks.Packaging , Microsoft.DotNet.Build.Tasks.Installers , Microsoft.DotNet.Build.Tasks.Feed , Microsoft.DotNet.Build.Tasks.Archives , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.ApiCompat , Microsoft.DotNet.CodeAnalysis , Microsoft.DotNet.XUnitConsoleRunner , Microsoft.DotNet.GenFacades , Microsoft.DotNet.GenAPI , Microsoft.DotNet.RemoteExecutor , Microsoft.DotNet.PackageTesting , Microsoft.DotNet.Helix.Sdk , Microsoft.DotNet.SharedFramework.Sdk From Version 7.0.0-beta.21507.3 -> To Version 7.0.0-beta.21512.3 * Update dependencies from https://github.com/dotnet/runtime-assets build 20211012.1 Microsoft.DotNet.CilStrip.Sources , System.ComponentModel.TypeConverter.TestData , System.Drawing.Common.TestData , System.IO.Compression.TestData , System.IO.Packaging.TestData , System.Net.TestData , System.Private.Runtime.UnicodeData , System.Runtime.Numerics.TestData , System.Runtime.TimeZoneData , System.Security.Cryptography.X509Certificates.TestData , System.Windows.Extensions.TestData From Version 7.0.0-beta.21506.1 -> To Version 7.0.0-beta.21512.1 * Update dependencies from https://github.com/dotnet/msquic build 20211012.1 System.Net.MsQuic.Transport From Version 6.0.0-preview.7.21480.1 -> To Version 6.0.0-preview.7.21512.1 * Update dependencies from https://github.com/dotnet/hotreload-utils build 20211012.2 Microsoft.DotNet.HotReload.Utils.Generator.BuildTool From Version 1.0.2-alpha.0.21504.1 -> To Version 1.0.2-alpha.0.21512.2 Co-authored-by: dotnet-maestro[bot] Co-authored-by: Alexander Köplinger --- .config/dotnet-tools.json | 2 +- eng/Version.Details.xml | 220 +++++++++++++++++++------------------- eng/Versions.props | 100 ++++++++--------- global.json | 10 +- 4 files changed, 166 insertions(+), 166 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 991875c53343e..d731c6dd1f16d 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "microsoft.dotnet.xharness.cli": { - "version": "1.0.0-prerelease.21508.1", + "version": "1.0.0-prerelease.21511.3", "commands": [ "xharness" ] diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 5cb354e673221..6b019983c5228 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,12 +1,12 @@ - + https://github.com/dotnet/icu - 848e383f845cee4ab9edb8b20b40154910792599 + ca9a6d29a62dd66925f5377d09b3d7e6afb2acd5 - + https://github.com/dotnet/msquic - 9b90833648c8f4f24bb79f362562779667888068 + e51511409822c6c87f47155ad2ded876727ead91 https://github.com/dotnet/emsdk @@ -18,225 +18,225 @@ - + https://github.com/dotnet/arcade - 98bacf3d68679c51adbb7c596a3de16a7f9acacf + dfb902f38e7c68d8cd83d53f32ea174e8f3a310a - + https://github.com/dotnet/arcade - 98bacf3d68679c51adbb7c596a3de16a7f9acacf + dfb902f38e7c68d8cd83d53f32ea174e8f3a310a - + https://github.com/dotnet/arcade - 98bacf3d68679c51adbb7c596a3de16a7f9acacf + dfb902f38e7c68d8cd83d53f32ea174e8f3a310a - + https://github.com/dotnet/arcade - 98bacf3d68679c51adbb7c596a3de16a7f9acacf + dfb902f38e7c68d8cd83d53f32ea174e8f3a310a - + https://github.com/dotnet/arcade - 98bacf3d68679c51adbb7c596a3de16a7f9acacf + dfb902f38e7c68d8cd83d53f32ea174e8f3a310a - + https://github.com/dotnet/arcade - 98bacf3d68679c51adbb7c596a3de16a7f9acacf + dfb902f38e7c68d8cd83d53f32ea174e8f3a310a - + https://github.com/dotnet/arcade - 98bacf3d68679c51adbb7c596a3de16a7f9acacf + dfb902f38e7c68d8cd83d53f32ea174e8f3a310a - + https://github.com/dotnet/arcade - 98bacf3d68679c51adbb7c596a3de16a7f9acacf + dfb902f38e7c68d8cd83d53f32ea174e8f3a310a - + https://github.com/dotnet/arcade - 98bacf3d68679c51adbb7c596a3de16a7f9acacf + dfb902f38e7c68d8cd83d53f32ea174e8f3a310a - + https://github.com/dotnet/arcade - 98bacf3d68679c51adbb7c596a3de16a7f9acacf + dfb902f38e7c68d8cd83d53f32ea174e8f3a310a - + https://github.com/dotnet/arcade - 98bacf3d68679c51adbb7c596a3de16a7f9acacf + dfb902f38e7c68d8cd83d53f32ea174e8f3a310a - + https://github.com/dotnet/arcade - 98bacf3d68679c51adbb7c596a3de16a7f9acacf + dfb902f38e7c68d8cd83d53f32ea174e8f3a310a - + https://github.com/dotnet/arcade - 98bacf3d68679c51adbb7c596a3de16a7f9acacf + dfb902f38e7c68d8cd83d53f32ea174e8f3a310a - + https://github.com/dotnet/arcade - 98bacf3d68679c51adbb7c596a3de16a7f9acacf + dfb902f38e7c68d8cd83d53f32ea174e8f3a310a - + https://github.com/dotnet/arcade - 98bacf3d68679c51adbb7c596a3de16a7f9acacf + dfb902f38e7c68d8cd83d53f32ea174e8f3a310a - + https://github.com/dotnet/arcade - 98bacf3d68679c51adbb7c596a3de16a7f9acacf + dfb902f38e7c68d8cd83d53f32ea174e8f3a310a - + https://github.com/dotnet/arcade - 98bacf3d68679c51adbb7c596a3de16a7f9acacf + dfb902f38e7c68d8cd83d53f32ea174e8f3a310a - + https://github.com/dotnet/arcade - 98bacf3d68679c51adbb7c596a3de16a7f9acacf + dfb902f38e7c68d8cd83d53f32ea174e8f3a310a https://github.com/microsoft/vstest 140434f7109d357d0158ade9e5164a4861513965 - + https://github.com/dotnet/runtime-assets - 256ee670d27044ab79756f3f955f7f1668f8000a + f800c64524ca148d3a5e8e9204fcbc9cf5055148 - + https://github.com/dotnet/runtime-assets - 256ee670d27044ab79756f3f955f7f1668f8000a + f800c64524ca148d3a5e8e9204fcbc9cf5055148 - + https://github.com/dotnet/runtime-assets - 256ee670d27044ab79756f3f955f7f1668f8000a + f800c64524ca148d3a5e8e9204fcbc9cf5055148 - + https://github.com/dotnet/runtime-assets - 256ee670d27044ab79756f3f955f7f1668f8000a + f800c64524ca148d3a5e8e9204fcbc9cf5055148 - + https://github.com/dotnet/runtime-assets - 256ee670d27044ab79756f3f955f7f1668f8000a + f800c64524ca148d3a5e8e9204fcbc9cf5055148 - + https://github.com/dotnet/runtime-assets - 256ee670d27044ab79756f3f955f7f1668f8000a + f800c64524ca148d3a5e8e9204fcbc9cf5055148 - + https://github.com/dotnet/runtime-assets - 256ee670d27044ab79756f3f955f7f1668f8000a + f800c64524ca148d3a5e8e9204fcbc9cf5055148 - + https://github.com/dotnet/runtime-assets - 256ee670d27044ab79756f3f955f7f1668f8000a + f800c64524ca148d3a5e8e9204fcbc9cf5055148 - + https://github.com/dotnet/runtime-assets - 256ee670d27044ab79756f3f955f7f1668f8000a + f800c64524ca148d3a5e8e9204fcbc9cf5055148 - + https://github.com/dotnet/runtime-assets - 256ee670d27044ab79756f3f955f7f1668f8000a + f800c64524ca148d3a5e8e9204fcbc9cf5055148 - + https://github.com/dotnet/llvm-project - fadaad69ceb75cd54372336dde2193a39348e740 + e20c0dbcf2fdc8979584b178cafd01db37121d64 - + https://github.com/dotnet/llvm-project - fadaad69ceb75cd54372336dde2193a39348e740 + e20c0dbcf2fdc8979584b178cafd01db37121d64 - + https://github.com/dotnet/llvm-project - fadaad69ceb75cd54372336dde2193a39348e740 + e20c0dbcf2fdc8979584b178cafd01db37121d64 - + https://github.com/dotnet/llvm-project - fadaad69ceb75cd54372336dde2193a39348e740 + e20c0dbcf2fdc8979584b178cafd01db37121d64 - + https://github.com/dotnet/llvm-project - fadaad69ceb75cd54372336dde2193a39348e740 + e20c0dbcf2fdc8979584b178cafd01db37121d64 - + https://github.com/dotnet/llvm-project - fadaad69ceb75cd54372336dde2193a39348e740 + e20c0dbcf2fdc8979584b178cafd01db37121d64 - + https://github.com/dotnet/llvm-project - fadaad69ceb75cd54372336dde2193a39348e740 + e20c0dbcf2fdc8979584b178cafd01db37121d64 - + https://github.com/dotnet/llvm-project - fadaad69ceb75cd54372336dde2193a39348e740 + e20c0dbcf2fdc8979584b178cafd01db37121d64 - + https://github.com/dotnet/runtime - 03e90a540cb2dfe5cab4086ea54ad5dd1f655749 + 565ff522bf630ff0556f3dc590dcb7337696a5d4 - + https://github.com/dotnet/runtime - 03e90a540cb2dfe5cab4086ea54ad5dd1f655749 + 565ff522bf630ff0556f3dc590dcb7337696a5d4 - + https://github.com/dotnet/runtime - 03e90a540cb2dfe5cab4086ea54ad5dd1f655749 + 565ff522bf630ff0556f3dc590dcb7337696a5d4 - + https://github.com/dotnet/runtime - 03e90a540cb2dfe5cab4086ea54ad5dd1f655749 + 565ff522bf630ff0556f3dc590dcb7337696a5d4 - + https://github.com/dotnet/runtime - 03e90a540cb2dfe5cab4086ea54ad5dd1f655749 + 565ff522bf630ff0556f3dc590dcb7337696a5d4 - + https://github.com/dotnet/runtime - 03e90a540cb2dfe5cab4086ea54ad5dd1f655749 + 565ff522bf630ff0556f3dc590dcb7337696a5d4 - + https://github.com/dotnet/runtime - 03e90a540cb2dfe5cab4086ea54ad5dd1f655749 + 565ff522bf630ff0556f3dc590dcb7337696a5d4 - + https://github.com/dotnet/runtime - 03e90a540cb2dfe5cab4086ea54ad5dd1f655749 + 565ff522bf630ff0556f3dc590dcb7337696a5d4 https://github.com/dotnet/linker 48d67e3d34016141825d99b2fcc1acdad65ce3bb - + https://github.com/dotnet/xharness - e7e0ec10c66f3c290e454a497a46cc41f8e59845 + 9eaf2814afcd7627059e5ea0ee2f5169a6409689 - + https://github.com/dotnet/xharness - e7e0ec10c66f3c290e454a497a46cc41f8e59845 + 9eaf2814afcd7627059e5ea0ee2f5169a6409689 - + https://github.com/dotnet/arcade - 98bacf3d68679c51adbb7c596a3de16a7f9acacf + dfb902f38e7c68d8cd83d53f32ea174e8f3a310a - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 7ad072e2561e4052c9f0ff0830b43834f0b763d4 + 2333857915a73bc993a9ec3c0481d2e772dd64b5 - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 7ad072e2561e4052c9f0ff0830b43834f0b763d4 + 2333857915a73bc993a9ec3c0481d2e772dd64b5 - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 7ad072e2561e4052c9f0ff0830b43834f0b763d4 + 2333857915a73bc993a9ec3c0481d2e772dd64b5 - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 7ad072e2561e4052c9f0ff0830b43834f0b763d4 + 2333857915a73bc993a9ec3c0481d2e772dd64b5 - + https://github.com/dotnet/hotreload-utils - 19751dd4e49b0acafa676a43f08cfe9ab838a9cd + 1aa5a751a0c659c90f51b9d6b23c8cbd9d0a8dbb - + https://github.com/dotnet/runtime-assets - 256ee670d27044ab79756f3f955f7f1668f8000a + f800c64524ca148d3a5e8e9204fcbc9cf5055148 https://github.com/dotnet/roslyn-analyzers diff --git a/eng/Versions.props b/eng/Versions.props index 34d70f76e22c5..d3834b1454473 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -56,30 +56,30 @@ 1.0.0-rc.2.21501.51 - 7.0.0-beta.21507.3 - 7.0.0-beta.21507.3 - 7.0.0-beta.21507.3 - 7.0.0-beta.21507.3 - 7.0.0-beta.21507.3 - 7.0.0-beta.21507.3 - 2.5.1-beta.21507.3 - 7.0.0-beta.21507.3 - 7.0.0-beta.21507.3 - 7.0.0-beta.21507.3 - 7.0.0-beta.21507.3 - 7.0.0-beta.21507.3 - 7.0.0-beta.21507.3 - 7.0.0-beta.21507.3 - 7.0.0-beta.21507.3 + 7.0.0-beta.21512.3 + 7.0.0-beta.21512.3 + 7.0.0-beta.21512.3 + 7.0.0-beta.21512.3 + 7.0.0-beta.21512.3 + 7.0.0-beta.21512.3 + 2.5.1-beta.21512.3 + 7.0.0-beta.21512.3 + 7.0.0-beta.21512.3 + 7.0.0-beta.21512.3 + 7.0.0-beta.21512.3 + 7.0.0-beta.21512.3 + 7.0.0-beta.21512.3 + 7.0.0-beta.21512.3 + 7.0.0-beta.21512.3 6.0.0-preview.1.102 - 7.0.0-alpha.1.21501.7 - 7.0.0-alpha.1.21501.7 - 7.0.0-alpha.1.21501.7 + 7.0.0-alpha.1.21511.2 + 7.0.0-alpha.1.21511.2 + 7.0.0-alpha.1.21511.2 3.1.0 - 7.0.0-alpha.1.21501.7 + 7.0.0-alpha.1.21511.2 5.0.0 4.3.0 @@ -113,28 +113,28 @@ 5.0.0 5.0.0 4.9.0-rc2.21473.1 - 7.0.0-alpha.1.21501.7 - 7.0.0-alpha.1.21501.7 + 7.0.0-alpha.1.21511.2 + 7.0.0-alpha.1.21511.2 4.5.4 4.5.0 - 7.0.0-alpha.1.21501.7 + 7.0.0-alpha.1.21511.2 - 7.0.0-beta.21506.1 - 7.0.0-beta.21506.1 - 7.0.0-beta.21506.1 - 7.0.0-beta.21506.1 - 7.0.0-beta.21506.1 - 7.0.0-beta.21506.1 - 7.0.0-beta.21506.1 - 7.0.0-beta.21506.1 - 7.0.0-beta.21506.1 - 7.0.0-beta.21506.1 - 7.0.0-beta.21506.1 + 7.0.0-beta.21512.1 + 7.0.0-beta.21512.1 + 7.0.0-beta.21512.1 + 7.0.0-beta.21512.1 + 7.0.0-beta.21512.1 + 7.0.0-beta.21512.1 + 7.0.0-beta.21512.1 + 7.0.0-beta.21512.1 + 7.0.0-beta.21512.1 + 7.0.0-beta.21512.1 + 7.0.0-beta.21512.1 - 1.0.0-prerelease.21507.4 - 1.0.0-prerelease.21507.4 - 1.0.0-prerelease.21507.4 - 1.0.0-prerelease.21507.4 + 1.0.0-prerelease.21512.6 + 1.0.0-prerelease.21512.6 + 1.0.0-prerelease.21512.6 + 1.0.0-prerelease.21512.6 16.9.0-beta1.21055.5 2.0.0-beta1.20253.1 @@ -158,9 +158,9 @@ 1.0.1-prerelease-00006 16.9.0-preview-20201201-01 - 1.0.0-prerelease.21508.1 - 1.0.0-prerelease.21508.1 - 1.0.2-alpha.0.21504.1 + 1.0.0-prerelease.21511.3 + 1.0.0-prerelease.21511.3 + 1.0.2-alpha.0.21512.2 2.4.2-pre.9 2.4.2 1.3.0 @@ -175,18 +175,18 @@ 7.0.100-1.21511.1 $(MicrosoftNETILLinkTasksVersion) - 7.0.0-alpha.1.21477.1 + 7.0.0-alpha.1.21511.1 - 6.0.0-preview.7.21480.1 + 6.0.0-preview.7.21512.1 - 11.1.0-alpha.1.21477.1 - 11.1.0-alpha.1.21477.1 - 11.1.0-alpha.1.21477.1 - 11.1.0-alpha.1.21477.1 - 11.1.0-alpha.1.21477.1 - 11.1.0-alpha.1.21477.1 - 11.1.0-alpha.1.21477.1 - 11.1.0-alpha.1.21477.1 + 11.1.0-alpha.1.21511.1 + 11.1.0-alpha.1.21511.1 + 11.1.0-alpha.1.21511.1 + 11.1.0-alpha.1.21511.1 + 11.1.0-alpha.1.21511.1 + 11.1.0-alpha.1.21511.1 + 11.1.0-alpha.1.21511.1 + 11.1.0-alpha.1.21511.1 6.0.0-rc.1.21416.1 $(MicrosoftNETWorkloadEmscriptenManifest60100Version) diff --git a/global.json b/global.json index df4e5c6ed44ba..baf0dace9db55 100644 --- a/global.json +++ b/global.json @@ -12,12 +12,12 @@ "python3": "3.7.1" }, "msbuild-sdks": { - "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "7.0.0-beta.21507.3", - "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.21507.3", - "Microsoft.DotNet.Helix.Sdk": "7.0.0-beta.21507.3", - "Microsoft.DotNet.SharedFramework.Sdk": "7.0.0-beta.21507.3", + "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "7.0.0-beta.21512.3", + "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.21512.3", + "Microsoft.DotNet.Helix.Sdk": "7.0.0-beta.21512.3", + "Microsoft.DotNet.SharedFramework.Sdk": "7.0.0-beta.21512.3", "Microsoft.Build.NoTargets": "3.1.0", "Microsoft.Build.Traversal": "3.0.23", - "Microsoft.NET.Sdk.IL": "7.0.0-alpha.1.21501.7" + "Microsoft.NET.Sdk.IL": "7.0.0-alpha.1.21511.2" } } From 0cb73e898c85f83a6394142542fc9e015556d811 Mon Sep 17 00:00:00 2001 From: Dong-Heon Jung Date: Thu, 14 Oct 2021 01:29:43 +0900 Subject: [PATCH 067/106] Add tizen armel build CI for pull request (#56281) * Add tizen amrel build CI * Fix some errors * Fix platform-matrix.yml * Skip tests for tizen-armel * Fix typo * Introduce a paramter to skip tests * Update osGroup to Tizen * Fix an error * Update fix --- eng/native/init-distro-rid.sh | 2 +- eng/pipelines/common/platform-matrix.yml | 27 +++++++++++++++++++ eng/pipelines/common/xplat-setup.yml | 4 +-- eng/pipelines/coreclr/templates/build-job.yml | 16 ++++++++--- eng/pipelines/runtime.yml | 1 + 5 files changed, 43 insertions(+), 7 deletions(-) diff --git a/eng/native/init-distro-rid.sh b/eng/native/init-distro-rid.sh index eea8eca2d26d1..5f6b194600124 100644 --- a/eng/native/init-distro-rid.sh +++ b/eng/native/init-distro-rid.sh @@ -41,7 +41,7 @@ initNonPortableDistroRid() # We have forced __PortableBuild=0. This is because -portablebuld # has been passed as false. if (( isPortable == 0 )); then - if [ "${ID}" = "rhel" || "${ID}" = "rocky" ]; then + if [ "${ID}" = "rhel" ] || [ "${ID}" = "rocky" ]; then # remove the last version digit VERSION_ID="${VERSION_ID%.*}" fi diff --git a/eng/pipelines/common/platform-matrix.yml b/eng/pipelines/common/platform-matrix.yml index efa204878083a..25a8ac7b913b7 100644 --- a/eng/pipelines/common/platform-matrix.yml +++ b/eng/pipelines/common/platform-matrix.yml @@ -654,6 +654,33 @@ jobs: helixQueueGroup: ${{ parameters.helixQueueGroup }} ${{ insert }}: ${{ parameters.jobParameters }} +# Tizen armel + +- ${{ if containsValue(parameters.platforms, 'Tizen_armel') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: Tizen + archType: armel + targetRid: tizen-armel + platform: Tizen_armel + container: + image: ubuntu-18.04-cross-armel-tizen-20210719212651-8b02f56 + registry: mcr + jobParameters: + runtimeFlavor: ${{ parameters.runtimeFlavor }} + stagedBuild: ${{ parameters.stagedBuild }} + buildConfig: ${{ parameters.buildConfig }} + ${{ if eq(parameters.passPlatforms, true) }}: + platforms: ${{ parameters.platforms }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + crossBuild: true + crossrootfsDir: '/crossrootfs/armel' + disableClrTest: true + ${{ insert }}: ${{ parameters.jobParameters }} + # Windows x64 - ${{ if or(containsValue(parameters.platforms, 'windows_x64'), in(parameters.platformGroup, 'all', 'gcstress')) }}: diff --git a/eng/pipelines/common/xplat-setup.yml b/eng/pipelines/common/xplat-setup.yml index 586a474175895..85a96ae5fce85 100644 --- a/eng/pipelines/common/xplat-setup.yml +++ b/eng/pipelines/common/xplat-setup.yml @@ -114,12 +114,12 @@ jobs: ${{ if eq(parameters.jobParameters.pool, '') }}: pool: # Public Linux Build Pool - ${{ if and(or(in(parameters.osGroup, 'Linux', 'FreeBSD', 'Android'), eq(parameters.hostedOs, 'Linux')), eq(variables['System.TeamProject'], 'public')) }}: + ${{ if and(or(in(parameters.osGroup, 'Linux', 'FreeBSD', 'Android', 'Tizen'), eq(parameters.hostedOs, 'Linux')), eq(variables['System.TeamProject'], 'public')) }}: name: NetCore1ESPool-Public demands: ImageOverride -equals Build.Ubuntu.1804.Amd64.Open # Official Build Linux Pool - ${{ if and(or(in(parameters.osGroup, 'Linux', 'FreeBSD', 'Browser', 'Android'), eq(parameters.hostedOs, 'Linux')), ne(variables['System.TeamProject'], 'public')) }}: + ${{ if and(or(in(parameters.osGroup, 'Linux', 'FreeBSD', 'Browser', 'Android', 'Tizen'), eq(parameters.hostedOs, 'Linux')), ne(variables['System.TeamProject'], 'public')) }}: name: NetCore1ESPool-Internal demands: ImageOverride -equals Build.Ubuntu.1804.Amd64 diff --git a/eng/pipelines/coreclr/templates/build-job.yml b/eng/pipelines/coreclr/templates/build-job.yml index e94d630ba2e95..c5efb8c77bdd2 100644 --- a/eng/pipelines/coreclr/templates/build-job.yml +++ b/eng/pipelines/coreclr/templates/build-job.yml @@ -9,6 +9,7 @@ parameters: crossBuild: false crossrootfsDir: '' dependOnEvaluatePaths: false + disableClrTest: false isOfficialBuild: false osGroup: '' osSubgroup: '' @@ -38,6 +39,7 @@ jobs: pool: ${{ parameters.pool }} condition: ${{ parameters.condition }} dependOnEvaluatePaths: ${{ parameters.dependOnEvaluatePaths }} + disableClrTest: ${{ parameters.disableClrTest }} pgoType: ${{ parameters.pgoType }} # Compute job name from template parameters @@ -141,6 +143,12 @@ jobs: - name: SignType value: $[ coalesce(variables.OfficialSignType, 'real') ] + - name: clrRuntimePortableBuildArg + value: '' + - ${{ if eq(parameters.archType, 'armel' )}}: + - name: clrRuntimePortableBuildArg + value: '-portablebuild=false' + - ${{ parameters.variables }} steps: @@ -188,7 +196,7 @@ jobs: # Build CoreCLR Runtime - ${{ if ne(parameters.osGroup, 'windows') }}: - - script: $(Build.SourcesDirectory)/src/coreclr/build-runtime$(scriptExt) $(buildConfig) $(archType) $(crossArg) $(osArg) -ci $(compilerArg) $(clrBuildPALTestsBuildArg) $(pgoInstrumentArg) $(officialBuildIdArg) $(clrInterpreterBuildArg) + - script: $(Build.SourcesDirectory)/src/coreclr/build-runtime$(scriptExt) $(buildConfig) $(archType) $(crossArg) $(osArg) -ci $(compilerArg) $(clrBuildPALTestsBuildArg) $(pgoInstrumentArg) $(officialBuildIdArg) $(clrInterpreterBuildArg) $(clrRuntimePortableBuildArg) displayName: Build CoreCLR Runtime - ${{ if eq(parameters.osGroup, 'windows') }}: - script: set __TestIntermediateDir=int&&$(Build.SourcesDirectory)/src/coreclr/build-runtime$(scriptExt) $(buildConfig) $(archType) -ci $(enforcePgoArg) $(pgoInstrumentArg) $(officialBuildIdArg) $(clrInterpreterBuildArg) @@ -210,7 +218,7 @@ jobs: displayName: Run CoreCLR Tools unit tests # Build native test components - - ${{ if ne(parameters.isOfficialBuild, true) }}: + - ${{ if and(ne(parameters.isOfficialBuild, true), ne(parameters.disableClrTest, true)) }}: - script: $(Build.SourcesDirectory)/src/tests/build$(scriptExt) skipmanaged skipgeneratelayout $(buildConfig) $(archType) $(crossArg) $(osArg) $(priorityArg) $(compilerArg) displayName: Build native test components @@ -257,7 +265,7 @@ jobs: condition: always() # Builds using gcc are not tested, and clrTools unitests do not publish the build artifacts - - ${{ if and(ne(parameters.compilerName, 'gcc'), ne(parameters.testGroup, 'clrTools')) }}: + - ${{ if and(ne(parameters.compilerName, 'gcc'), ne(parameters.testGroup, 'clrTools'), ne(parameters.disableClrTest, true)) }}: # Publish product output directory for consumption by tests. - template: /eng/pipelines/common/upload-artifact-step.yml parameters: @@ -276,7 +284,7 @@ jobs: osGroup: ${{ parameters.osGroup }} osSubgroup: ${{ parameters.osSubgroup }} - - ${{ if and(ne(parameters.compilerName, 'gcc'), ne(parameters.testGroup, ''), ne(parameters.testGroup, 'clrTools')) }}: + - ${{ if and(ne(parameters.compilerName, 'gcc'), ne(parameters.testGroup, ''), ne(parameters.testGroup, 'clrTools'), ne(parameters.disableClrTest, true)) }}: # Publish test native components for consumption by test execution. - ${{ if and(ne(parameters.isOfficialBuild, true), eq(parameters.pgoType, '')) }}: - template: /eng/pipelines/common/upload-artifact-step.yml diff --git a/eng/pipelines/runtime.yml b/eng/pipelines/runtime.yml index ddeca884f3229..b18a7ae71393b 100644 --- a/eng/pipelines/runtime.yml +++ b/eng/pipelines/runtime.yml @@ -79,6 +79,7 @@ jobs: - Linux_musl_arm64 - Linux_musl_x64 - OSX_arm64 + - Tizen_armel - windows_x86 - windows_x64 - windows_arm From 49cf05cdea3baa22592d08d74ce2f0e2c32786a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Wed, 13 Oct 2021 19:23:00 +0200 Subject: [PATCH 068/106] Don't include s390x in coreclr outerloop runs (#60351) This was a copy-paste mistake, see https://github.com/dotnet/runtime/pull/60255#issuecomment-942539639 --- eng/pipelines/common/platform-matrix.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/pipelines/common/platform-matrix.yml b/eng/pipelines/common/platform-matrix.yml index 25a8ac7b913b7..ff13637a92a14 100644 --- a/eng/pipelines/common/platform-matrix.yml +++ b/eng/pipelines/common/platform-matrix.yml @@ -211,7 +211,7 @@ jobs: # Linux s390x -- ${{ if or(containsValue(parameters.platforms, 'Linux_s390x'), in(parameters.platformGroup, 'all', 'gcstress')) }}: +- ${{ if containsValue(parameters.platforms, 'Linux_s390x') }}: - template: xplat-setup.yml parameters: jobTemplate: ${{ parameters.jobTemplate }} From c2f287c945a17e9a49fad57d89d0936bad697e80 Mon Sep 17 00:00:00 2001 From: LateApexEarlySpeed <72254037+lateapexearlyspeed@users.noreply.github.com> Date: Thu, 14 Oct 2021 02:17:14 +0800 Subject: [PATCH 069/106] Support unseekable filestream when ReadAllBytes[Async] (#58434) Co-authored-by: Adam Sitnik --- .../tests/File/ReadWriteAllBytes.cs | 59 +++++++++++++++++++ .../tests/File/ReadWriteAllBytesAsync.cs | 58 ++++++++++++++++++ ...andle.OverlappedValueTaskSource.Windows.cs | 7 ++- .../src/System/IO/File.cs | 12 ++-- .../src/System/IO/RandomAccess.Windows.cs | 16 ++--- 5 files changed, 137 insertions(+), 15 deletions(-) diff --git a/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllBytes.cs b/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllBytes.cs index 54c7643766335..bb94b18b3115e 100644 --- a/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllBytes.cs +++ b/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllBytes.cs @@ -2,8 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Text; +using System.Threading; using System.Threading.Tasks; using Xunit; +using System.IO.Pipes; +using Microsoft.DotNet.XUnitExtensions; namespace System.IO.Tests { @@ -172,5 +175,61 @@ public void ProcFs_NotEmpty(string path) { Assert.InRange(File.ReadAllBytes(path).Length, 1, int.MaxValue); } + + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] // DOS device paths (\\.\ and \\?\) are a Windows concept + public async Task ReadAllBytes_NonSeekableFileStream_InWindows() + { + string pipeName = FileSystemTest.GetNamedPipeServerStreamName(); + string pipePath = Path.GetFullPath($@"\\.\pipe\{pipeName}"); + + var namedPipeWriterStream = new NamedPipeServerStream(pipeName, PipeDirection.Out); + var contentBytes = new byte[] { 1, 2, 3 }; + + using (var cts = new CancellationTokenSource()) + { + Task writingServerTask = WaitConnectionAndWritePipeStreamAsync(namedPipeWriterStream, contentBytes, cts.Token); + Task readTask = Task.Run(() => File.ReadAllBytes(pipePath), cts.Token); + cts.CancelAfter(TimeSpan.FromSeconds(50)); + + await writingServerTask; + byte[] readBytes = await readTask; + Assert.Equal(contentBytes, readBytes); + } + + static async Task WaitConnectionAndWritePipeStreamAsync(NamedPipeServerStream namedPipeWriterStream, byte[] contentBytes, CancellationToken cancellationToken) + { + await using (namedPipeWriterStream) + { + await namedPipeWriterStream.WaitForConnectionAsync(cancellationToken); + await namedPipeWriterStream.WriteAsync(contentBytes, cancellationToken); + } + } + } + + [Fact] + [PlatformSpecific(TestPlatforms.AnyUnix & ~TestPlatforms.Browser)] + public async Task ReadAllBytes_NonSeekableFileStream_InUnix() + { + string fifoPath = GetTestFilePath(); + Assert.Equal(0, mkfifo(fifoPath, 438 /* 666 in octal */ )); + + var contentBytes = new byte[] { 1, 2, 3 }; + + await Task.WhenAll( + Task.Run(() => + { + byte[] readBytes = File.ReadAllBytes(fifoPath); + Assert.Equal(contentBytes, readBytes); + }), + Task.Run(() => + { + using var fs = new FileStream(fifoPath, FileMode.Open, FileAccess.Write, FileShare.Read); + foreach (byte content in contentBytes) + { + fs.WriteByte(content); + } + })); + } } } diff --git a/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllBytesAsync.cs b/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllBytesAsync.cs index 748c01dbffb8f..be562f15ca953 100644 --- a/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllBytesAsync.cs +++ b/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllBytesAsync.cs @@ -5,6 +5,8 @@ using System.Threading; using System.Threading.Tasks; using Xunit; +using System.IO.Pipes; +using Microsoft.DotNet.XUnitExtensions; namespace System.IO.Tests { @@ -186,5 +188,61 @@ public async Task ProcFs_NotEmpty(string path) { Assert.InRange((await File.ReadAllBytesAsync(path)).Length, 1, int.MaxValue); } + + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] // DOS device paths (\\.\ and \\?\) are a Windows concept + public async Task ReadAllBytesAsync_NonSeekableFileStream_InWindows() + { + string pipeName = FileSystemTest.GetNamedPipeServerStreamName(); + string pipePath = Path.GetFullPath($@"\\.\pipe\{pipeName}"); + + var namedPipeWriterStream = new NamedPipeServerStream(pipeName, PipeDirection.Out); + var contentBytes = new byte[] { 1, 2, 3 }; + + using (var cts = new CancellationTokenSource()) + { + Task writingServerTask = WaitConnectionAndWritePipeStreamAsync(namedPipeWriterStream, contentBytes, cts.Token); + Task readTask = File.ReadAllBytesAsync(pipePath, cts.Token); + cts.CancelAfter(TimeSpan.FromSeconds(50)); + + await writingServerTask; + byte[] readBytes = await readTask; + Assert.Equal(contentBytes, readBytes); + } + + static async Task WaitConnectionAndWritePipeStreamAsync(NamedPipeServerStream namedPipeWriterStream, byte[] contentBytes, CancellationToken cancellationToken) + { + await using (namedPipeWriterStream) + { + await namedPipeWriterStream.WaitForConnectionAsync(cancellationToken); + await namedPipeWriterStream.WriteAsync(contentBytes, cancellationToken); + } + } + } + + [Fact] + [PlatformSpecific(TestPlatforms.AnyUnix & ~TestPlatforms.Browser)] + public async Task ReadAllBytesAsync_NonSeekableFileStream_InUnix() + { + string fifoPath = GetTestFilePath(); + Assert.Equal(0, mkfifo(fifoPath, 438 /* 666 in octal */ )); + + var contentBytes = new byte[] { 1, 2, 3 }; + + await Task.WhenAll( + Task.Run(async () => + { + byte[] readBytes = await File.ReadAllBytesAsync(fifoPath); + Assert.Equal(contentBytes, readBytes); + }), + Task.Run(() => + { + using var fs = new FileStream(fifoPath, FileMode.Open, FileAccess.Write, FileShare.Read); + foreach (byte content in contentBytes) + { + fs.WriteByte(content); + } + })); + } } } diff --git a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.OverlappedValueTaskSource.Windows.cs b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.OverlappedValueTaskSource.Windows.cs index 9f689b161bbd0..c2eab851bd60e 100644 --- a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.OverlappedValueTaskSource.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.OverlappedValueTaskSource.Windows.cs @@ -86,8 +86,11 @@ internal static Exception GetIOError(int errorCode, string? path) _bufferSize = memory.Length; _memoryHandle = memory.Pin(); _overlapped = _fileHandle.ThreadPoolBinding!.AllocateNativeOverlapped(_preallocatedOverlapped); - _overlapped->OffsetLow = (int)fileOffset; - _overlapped->OffsetHigh = (int)(fileOffset >> 32); + if (_fileHandle.CanSeek) + { + _overlapped->OffsetLow = (int)fileOffset; + _overlapped->OffsetHigh = (int)(fileOffset >> 32); + } return _overlapped; } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/File.cs b/src/libraries/System.Private.CoreLib/src/System/IO/File.cs index 02d0dfac8f9b6..4efdb1366d7a0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/File.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/File.cs @@ -326,14 +326,14 @@ public static byte[] ReadAllBytes(string path) // bufferSize == 1 used to avoid unnecessary buffer in FileStream using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 1, FileOptions.SequentialScan)) { - long fileLength = fs.Length; - if (fileLength > int.MaxValue) + long fileLength = 0; + if (fs.CanSeek && (fileLength = fs.Length) > int.MaxValue) { throw new IOException(SR.IO_FileTooLong2GB); } - else if (fileLength == 0) + if (fileLength == 0) { - // Some file systems (e.g. procfs on Linux) return 0 for length even when there's content. + // Some file systems (e.g. procfs on Linux) return 0 for length even when there's content; also there is non-seekable file stream. // Thus we need to assume 0 doesn't mean empty. return ReadAllBytesUnknownLength(fs); } @@ -711,8 +711,8 @@ private static async Task InternalReadAllTextAsync(string path, Encoding bool returningInternalTask = false; try { - long fileLength = fs.Length; - if (fileLength > int.MaxValue) + long fileLength = 0L; + if (fs.CanSeek && (fileLength = fs.Length) > int.MaxValue) { var e = new IOException(SR.IO_FileTooLong2GB); ExceptionDispatchInfo.SetCurrentStackTrace(e); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs index 31adaca38fe5e..7f6aed824c847 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs @@ -73,7 +73,7 @@ private static unsafe int ReadSyncUsingAsyncHandle(SafeFileHandle handle, SpanOffsetLow = unchecked((int)fileOffset); - result->OffsetHigh = (int)(fileOffset >> 32); + if (handle.CanSeek) + { + result->OffsetLow = unchecked((int)fileOffset); + result->OffsetHigh = (int)(fileOffset >> 32); + } // From https://docs.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-getoverlappedresult: // "If the hEvent member of the OVERLAPPED structure is NULL, the system uses the state of the hFile handle to signal when the operation has been completed. From 5b1ebf72747f530f59427783ce73501cd840a279 Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Wed, 13 Oct 2021 12:07:09 -0700 Subject: [PATCH 070/106] Fix CRC32 encoding (#60329) On x64, when the crc32 instruction 2nd operand is a memory address (such as for a static field), and that address is containable (which normally doesn't happen, because the address will be above the 4GB lower address space), then the instruction was being improperly encoded. --- src/coreclr/jit/emitxarch.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/emitxarch.cpp b/src/coreclr/jit/emitxarch.cpp index dda2f217a7f7b..494677f995038 100644 --- a/src/coreclr/jit/emitxarch.cpp +++ b/src/coreclr/jit/emitxarch.cpp @@ -10292,7 +10292,7 @@ BYTE* emitter::emitOutputAM(BYTE* dst, instrDesc* id, code_t code, CnsVal* addc) noway_assert((int)dsp == dsp); // This requires, specifying a SIB byte after ModRM byte. - if (EncodedBySSE38orSSE3A(ins)) + if (EncodedBySSE38orSSE3A(ins) || (ins == INS_crc32)) { dst += emitOutputByte(dst, code | 0x04); } From 51c16ae0281632d6e29b273bec9fdca43d61d952 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Wed, 13 Oct 2021 22:01:51 +0200 Subject: [PATCH 071/106] Fix System.Numerics.Vectors tests on iOS and other FullAOT targets (#60335) * Fix System.Numerics.Vectors tests on iOS and other FullAOT targets Instead of using dynamic we can use explicit type checks which don't require runtime code generation. Also fixed running just a subset of xunit tests on Apple targets in tests.mobile.targets. * Disable tests that fail on x86 due to a Mono runtime asserts See https://github.com/dotnet/runtime/issues/60347 --- eng/testing/tests.mobile.targets | 6 + .../TestUtilities/System/PlatformDetection.cs | 2 + .../tests/GenericVectorTests.cs | 40 ++++ .../System.Numerics.Vectors/tests/Util.cs | 207 +++++++++++++++--- 4 files changed, 225 insertions(+), 30 deletions(-) diff --git a/eng/testing/tests.mobile.targets b/eng/testing/tests.mobile.targets index 58dbfac94db89..acce583b8a96c 100644 --- a/eng/testing/tests.mobile.targets +++ b/eng/testing/tests.mobile.targets @@ -42,6 +42,12 @@ $(AdditionalXHarnessArguments) --arg=-c=$(XUnitClassName) + + + $(AdditionalXHarnessArguments) -- -m=$(XUnitMethodName) + $(AdditionalXHarnessArguments) -- -c=$(XUnitClassName) + + diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs index baec379eece8b..c4e2e657c07ab 100644 --- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs +++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs @@ -48,6 +48,8 @@ public static partial class PlatformDetection public static bool IsNotArm64Process => !IsArm64Process; public static bool IsArmOrArm64Process => IsArmProcess || IsArm64Process; public static bool IsNotArmNorArm64Process => !IsArmOrArm64Process; + public static bool IsX86Process => RuntimeInformation.ProcessArchitecture == Architecture.X86; + public static bool IsNotX86Process => !IsX86Process; public static bool IsArgIteratorSupported => IsMonoRuntime || (IsWindows && IsNotArmProcess); public static bool IsArgIteratorNotSupported => !IsArgIteratorSupported; public static bool Is32BitProcess => IntPtr.Size == 4; diff --git a/src/libraries/System.Numerics.Vectors/tests/GenericVectorTests.cs b/src/libraries/System.Numerics.Vectors/tests/GenericVectorTests.cs index f861739278f95..556e6672b806c 100644 --- a/src/libraries/System.Numerics.Vectors/tests/GenericVectorTests.cs +++ b/src/libraries/System.Numerics.Vectors/tests/GenericVectorTests.cs @@ -958,8 +958,10 @@ private void TestToString(string format, IFormatProvider provider) where T : [Fact] public void AdditionInt64() { TestAddition(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void AdditionSingle() { TestAddition(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void AdditionDouble() { TestAddition(); } private void TestAddition() where T : struct { @@ -1023,8 +1025,10 @@ private void TestAdditionOverflow() where T : struct [Fact] public void SubtractionInt64() { TestSubtraction(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void SubtractionSingle() { TestSubtraction(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void SubtractionDouble() { TestSubtraction(); } private void TestSubtraction() where T : struct { @@ -1088,8 +1092,10 @@ private void TestSubtractionOverflow() where T : struct [Fact] public void MultiplicationInt64() { TestMultiplication(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void MultiplicationSingle() { TestMultiplication(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void MultiplicationDouble() { TestMultiplication(); } private void TestMultiplication() where T : struct { @@ -1122,8 +1128,10 @@ private void TestMultiplication() where T : struct [Fact] public void MultiplicationWithScalarInt64() { TestMultiplicationWithScalar(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void MultiplicationWithScalarSingle() { TestMultiplicationWithScalar(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void MultiplicationWithScalarDouble() { TestMultiplicationWithScalar(); } private void TestMultiplicationWithScalar() where T : struct { @@ -1164,8 +1172,10 @@ private void TestMultiplicationWithScalar() where T : struct [Fact] public void DivisionInt64() { TestDivision(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void DivisionSingle() { TestDivision(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void DivisionDouble() { TestDivision(); } private void TestDivision() where T : struct { @@ -1224,8 +1234,10 @@ private void TestDivisionByZeroException() where T : struct [Fact] public void UnaryMinusInt64() { TestUnaryMinus(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void UnaryMinusSingle() { TestUnaryMinus(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void UnaryMinusDouble() { TestUnaryMinus(); } private void TestUnaryMinus() where T : struct { @@ -1424,8 +1436,10 @@ private void TestBitwiseAndNot() where T : struct [Fact] public void VectorGreaterThanInt64() { TestVectorGreaterThan(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void VectorGreaterThanSingle() { TestVectorGreaterThan(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void VectorGreaterThanDouble() { TestVectorGreaterThan(); } private void TestVectorGreaterThan() where T : struct { @@ -1461,8 +1475,10 @@ private void TestVectorGreaterThan() where T : struct [Fact] public void GreaterThanOrEqualInt64() { TestVectorGreaterThanOrEqual(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void GreaterThanOrEqualSingle() { TestVectorGreaterThanOrEqual(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void GreaterThanOrEqualDouble() { TestVectorGreaterThanOrEqual(); } private void TestVectorGreaterThanOrEqual() where T : struct { @@ -1714,8 +1730,10 @@ private void TestVectorGreaterThanOrEqualAll() where T : struct [Fact] public void LessThanInt64() { TestVectorLessThan(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void LessThanSingle() { TestVectorLessThan(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void LessThanDouble() { TestVectorLessThan(); } private void TestVectorLessThan() where T : struct { @@ -1751,8 +1769,10 @@ private void TestVectorLessThan() where T : struct [Fact] public void LessThanOrEqualInt64() { TestVectorLessThanOrEqual(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void LessThanOrEqualSingle() { TestVectorLessThanOrEqual(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void LessThanOrEqualDouble() { TestVectorLessThanOrEqual(); } private void TestVectorLessThanOrEqual() where T : struct { @@ -1788,8 +1808,10 @@ private void TestVectorLessThanOrEqual() where T : struct [Fact] public void LessThanAnyInt64() { TestVectorLessThanAny(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void LessThanAnySingle() { TestVectorLessThanAny(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void LessThanAnyDouble() { TestVectorLessThanAny(); } private void TestVectorLessThanAny() where T : struct { @@ -2114,8 +2136,10 @@ private void TestVectorEqualsAll() where T : struct [Fact] public void ConditionalSelectInt64() { TestConditionalSelect(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void ConditionalSelectSingle() { TestConditionalSelect(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void ConditionalSelectDouble() { TestConditionalSelect(); } private void TestConditionalSelect() where T : struct { @@ -2166,8 +2190,10 @@ private void TestConditionalSelect() where T : struct [Fact] public void DotProductInt64() { TestDotProduct(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void DotProductSingle() { TestDotProduct(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void DotProductDouble() { TestDotProduct(); } private void TestDotProduct() where T : struct { @@ -2202,8 +2228,10 @@ private void TestDotProduct() where T : struct [Fact] public void MaxInt64() { TestMax(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void MaxSingle() { TestMax(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void MaxDouble() { TestMax(); } private void TestMax() where T : struct { @@ -2238,8 +2266,10 @@ private void TestMax() where T : struct [Fact] public void MinInt64() { TestMin(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void MinSingle() { TestMin(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void MinDouble() { TestMin(); } private void TestMin() where T : struct { @@ -2274,8 +2304,10 @@ private void TestMin() where T : struct [Fact] public void SquareRootInt64() { TestSquareRoot(-1); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void SquareRootSingle() { TestSquareRoot(6); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void SquareRootDouble() { TestSquareRoot(15); } private void TestSquareRoot(int precision = -1) where T : struct, IEquatable { @@ -2368,8 +2400,10 @@ public void FloorDouble() [Fact] public void AbsInt64() { TestAbs(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void AbsSingle() { TestAbs(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void AbsDouble() { TestAbs(); } private void TestAbs() where T : struct { @@ -2406,8 +2440,10 @@ private void TestAbs() where T : struct [Fact] public void MultiplicationReflectionInt64() { TestMultiplicationReflection(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void MultiplicationReflectionSingle() { TestMultiplicationReflection(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void MultiplicationReflectionDouble() { TestMultiplicationReflection(); } private void TestMultiplicationReflection() where T : struct { @@ -2443,8 +2479,10 @@ private void TestMultiplicationReflection() where T : struct [Fact] public void AdditionReflectionInt64() { TestAdditionReflection(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void AdditionReflectionSingle() { TestAdditionReflection(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void AdditionReflectionDouble() { TestAdditionReflection(); } private void TestAdditionReflection() where T : struct { @@ -2480,8 +2518,10 @@ private void TestAdditionReflection() where T : struct [Fact] public void DivisionReflectionInt64() { TestDivisionReflection(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void DivisionReflectionSingle() { TestDivisionReflection(); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/60347", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsX86Process))] public void DivisionReflectionDouble() { TestDivisionReflection(); } private void TestDivisionReflection() where T : struct { diff --git a/src/libraries/System.Numerics.Vectors/tests/Util.cs b/src/libraries/System.Numerics.Vectors/tests/Util.cs index 4fb55a716afd1..135b3fc0e65e4 100644 --- a/src/libraries/System.Numerics.Vectors/tests/Util.cs +++ b/src/libraries/System.Numerics.Vectors/tests/Util.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Linq; - namespace System.Numerics.Tests { public static class Util @@ -93,57 +91,150 @@ public static T GenerateSingleValue(int min = 1, int max = 100) where T : str public static T Abs(T value) where T : struct { - Type[] unsignedTypes = new[] { typeof(byte), typeof(ushort), typeof(uint), typeof(ulong) }; - if (unsignedTypes.Contains(typeof(T))) - { - return value; - } - - dynamic dyn = (dynamic)value; - var abs = Math.Abs(dyn); - T ret = (T)abs; - return ret; + // unsigned types + if (value is byte) return value; + else if (value is ushort) return value; + else if (value is uint) return value; + else if (value is ulong) return value; + // signed types + else if (value is short) return (T)(ValueType)(short) ( Math.Abs((short) (ValueType)value) ); + else if (value is int) return (T)(ValueType)(int) ( Math.Abs((int) (ValueType)value) ); + else if (value is long) return (T)(ValueType)(long) ( Math.Abs((long) (ValueType)value) ); + else if (value is sbyte) return (T)(ValueType)(sbyte) ( Math.Abs((sbyte) (ValueType)value) ); + else if (value is float) return (T)(ValueType)(float) ( Math.Abs((float) (ValueType)value) ); + else if (value is double) return (T)(ValueType)(double) ( Math.Abs((double)(ValueType)value) ); + else throw new NotImplementedException(); } public static T Sqrt(T value) where T : struct { - return unchecked((T)(dynamic)(Math.Sqrt((dynamic)value))); + unchecked + { + if (value is short) return (T)(ValueType)(short) ( Math.Sqrt((short) (ValueType)value) ); + else if (value is int) return (T)(ValueType)(int) ( Math.Sqrt((int) (ValueType)value) ); + else if (value is long) return (T)(ValueType)(long) ( Math.Sqrt((long) (ValueType)value) ); + else if (value is ushort) return (T)(ValueType)(ushort) ( Math.Sqrt((ushort)(ValueType)value) ); + else if (value is uint) return (T)(ValueType)(uint) ( Math.Sqrt((uint) (ValueType)value) ); + else if (value is ulong) return (T)(ValueType)(ulong) ( Math.Sqrt((ulong) (ValueType)value) ); + else if (value is byte) return (T)(ValueType)(byte) ( Math.Sqrt((byte) (ValueType)value) ); + else if (value is sbyte) return (T)(ValueType)(sbyte) ( Math.Sqrt((sbyte) (ValueType)value) ); + else if (value is float) return (T)(ValueType)(float) ( Math.Sqrt((float) (ValueType)value) ); + else if (value is double) return (T)(ValueType)(double) ( Math.Sqrt((double)(ValueType)value) ); + else throw new NotImplementedException(); + } } public static T Multiply(T left, T right) where T : struct { - return unchecked((T)((dynamic)left * right)); + unchecked + { + if (left is short) return (T)(ValueType)(short) ( (short) (ValueType)left * (short) (ValueType)right ); + else if (left is int) return (T)(ValueType)(int) ( (int) (ValueType)left * (int) (ValueType)right ); + else if (left is long) return (T)(ValueType)(long) ( (long) (ValueType)left * (long) (ValueType)right ); + else if (left is ushort) return (T)(ValueType)(ushort) ( (ushort)(ValueType)left * (ushort)(ValueType)right ); + else if (left is uint) return (T)(ValueType)(uint) ( (uint) (ValueType)left * (uint) (ValueType)right ); + else if (left is ulong) return (T)(ValueType)(ulong) ( (ulong) (ValueType)left * (ulong) (ValueType)right ); + else if (left is byte) return (T)(ValueType)(byte) ( (byte) (ValueType)left * (byte) (ValueType)right ); + else if (left is sbyte) return (T)(ValueType)(sbyte) ( (sbyte) (ValueType)left * (sbyte) (ValueType)right ); + else if (left is float) return (T)(ValueType)(float) ( (float) (ValueType)left * (float) (ValueType)right ); + else if (left is double) return (T)(ValueType)(double) ( (double)(ValueType)left * (double)(ValueType)right ); + else throw new NotImplementedException(); + } } public static T Divide(T left, T right) where T : struct { - return (T)((dynamic)left / right); + if (left is short) return (T)(ValueType)(short) ( (short) (ValueType)left / (short) (ValueType)right ); + else if (left is int) return (T)(ValueType)(int) ( (int) (ValueType)left / (int) (ValueType)right ); + else if (left is long) return (T)(ValueType)(long) ( (long) (ValueType)left / (long) (ValueType)right ); + else if (left is ushort) return (T)(ValueType)(ushort) ( (ushort)(ValueType)left / (ushort)(ValueType)right ); + else if (left is uint) return (T)(ValueType)(uint) ( (uint) (ValueType)left / (uint) (ValueType)right ); + else if (left is ulong) return (T)(ValueType)(ulong) ( (ulong) (ValueType)left / (ulong) (ValueType)right ); + else if (left is byte) return (T)(ValueType)(byte) ( (byte) (ValueType)left / (byte) (ValueType)right ); + else if (left is sbyte) return (T)(ValueType)(sbyte) ( (sbyte) (ValueType)left / (sbyte) (ValueType)right ); + else if (left is float) return (T)(ValueType)(float) ( (float) (ValueType)left / (float) (ValueType)right ); + else if (left is double) return (T)(ValueType)(double) ( (double)(ValueType)left / (double)(ValueType)right ); + else throw new NotImplementedException(); } public static T Add(T left, T right) where T : struct { - return unchecked((T)((dynamic)left + right)); + unchecked + { + if (left is short) return (T)(ValueType)(short) ( (short) (ValueType)left + (short) (ValueType)right ); + else if (left is int) return (T)(ValueType)(int) ( (int) (ValueType)left + (int) (ValueType)right ); + else if (left is long) return (T)(ValueType)(long) ( (long) (ValueType)left + (long) (ValueType)right ); + else if (left is ushort) return (T)(ValueType)(ushort) ( (ushort)(ValueType)left + (ushort)(ValueType)right ); + else if (left is uint) return (T)(ValueType)(uint) ( (uint) (ValueType)left + (uint) (ValueType)right ); + else if (left is ulong) return (T)(ValueType)(ulong) ( (ulong) (ValueType)left + (ulong) (ValueType)right ); + else if (left is byte) return (T)(ValueType)(byte) ( (byte) (ValueType)left + (byte) (ValueType)right ); + else if (left is sbyte) return (T)(ValueType)(sbyte) ( (sbyte) (ValueType)left + (sbyte) (ValueType)right ); + else if (left is float) return (T)(ValueType)(float) ( (float) (ValueType)left + (float) (ValueType)right ); + else if (left is double) return (T)(ValueType)(double) ( (double)(ValueType)left + (double)(ValueType)right ); + else throw new NotImplementedException(); + } } public static T Subtract(T left, T right) where T : struct { - return unchecked((T)((dynamic)left - right)); + unchecked + { + if (left is short) return (T)(ValueType)(short) ( (short) (ValueType)left - (short) (ValueType)right ); + else if (left is int) return (T)(ValueType)(int) ( (int) (ValueType)left - (int) (ValueType)right ); + else if (left is long) return (T)(ValueType)(long) ( (long) (ValueType)left - (long) (ValueType)right ); + else if (left is ushort) return (T)(ValueType)(ushort) ( (ushort)(ValueType)left - (ushort)(ValueType)right ); + else if (left is uint) return (T)(ValueType)(uint) ( (uint) (ValueType)left - (uint) (ValueType)right ); + else if (left is ulong) return (T)(ValueType)(ulong) ( (ulong) (ValueType)left - (ulong) (ValueType)right ); + else if (left is byte) return (T)(ValueType)(byte) ( (byte) (ValueType)left - (byte) (ValueType)right ); + else if (left is sbyte) return (T)(ValueType)(sbyte) ( (sbyte) (ValueType)left - (sbyte) (ValueType)right ); + else if (left is float) return (T)(ValueType)(float) ( (float) (ValueType)left - (float) (ValueType)right ); + else if (left is double) return (T)(ValueType)(double) ( (double)(ValueType)left - (double)(ValueType)right ); + else throw new NotImplementedException(); + } } public static T Xor(T left, T right) where T : struct { - return (T)((dynamic)left ^ right); + if (left is short) return (T)(ValueType)(short) ( (short) (ValueType)left ^ (short) (ValueType)right ); + else if (left is int) return (T)(ValueType)(int) ( (int) (ValueType)left ^ (int) (ValueType)right ); + else if (left is long) return (T)(ValueType)(long) ( (long) (ValueType)left ^ (long) (ValueType)right ); + else if (left is ushort) return (T)(ValueType)(ushort) ( (ushort)(ValueType)left ^ (ushort)(ValueType)right ); + else if (left is uint) return (T)(ValueType)(uint) ( (uint) (ValueType)left ^ (uint) (ValueType)right ); + else if (left is ulong) return (T)(ValueType)(ulong) ( (ulong) (ValueType)left ^ (ulong) (ValueType)right ); + else if (left is byte) return (T)(ValueType)(byte) ( (byte) (ValueType)left ^ (byte) (ValueType)right ); + else if (left is sbyte) return (T)(ValueType)(sbyte) ( (sbyte) (ValueType)left ^ (sbyte) (ValueType)right ); + else throw new NotImplementedException(); } public static T AndNot(T left, T right) where T : struct { - return (T)((dynamic)left & ~(dynamic)right); + if (left is short) return (T)(ValueType)(short) ( (short) (ValueType)left & ~(short) (ValueType)right ); + else if (left is int) return (T)(ValueType)(int) ( (int) (ValueType)left & ~(int) (ValueType)right ); + else if (left is long) return (T)(ValueType)(long) ( (long) (ValueType)left & ~(long) (ValueType)right ); + else if (left is ushort) return (T)(ValueType)(ushort) ( (ushort)(ValueType)left & ~(ushort)(ValueType)right ); + else if (left is uint) return (T)(ValueType)(uint) ( (uint) (ValueType)left & ~(uint) (ValueType)right ); + else if (left is ulong) return (T)(ValueType)(ulong) ( (ulong) (ValueType)left & ~(ulong) (ValueType)right ); + else if (left is byte) return (T)(ValueType)(byte) ( (byte) (ValueType)left & ~(byte) (ValueType)right ); + else if (left is sbyte) return (T)(ValueType)(sbyte) ( (sbyte) (ValueType)left & ~(sbyte) (ValueType)right ); + else throw new NotImplementedException(); } public static T OnesComplement(T left) where T : struct { - return unchecked((T)(~(dynamic)left)); + unchecked + { + if (left is short) return (T)(ValueType)(short) ( ~(short) (ValueType)left ); + else if (left is int) return (T)(ValueType)(int) ( ~(int) (ValueType)left ); + else if (left is long) return (T)(ValueType)(long) ( ~(long) (ValueType)left ); + else if (left is ushort) return (T)(ValueType)(ushort) ( ~(ushort)(ValueType)left ); + else if (left is uint) return (T)(ValueType)(uint) ( ~(uint) (ValueType)left ); + else if (left is ulong) return (T)(ValueType)(ulong) ( ~(ulong) (ValueType)left ); + else if (left is byte) return (T)(ValueType)(byte) ( ~(byte) (ValueType)left ); + else if (left is sbyte) return (T)(ValueType)(sbyte) ( ~(sbyte) (ValueType)left ); + else throw new NotImplementedException(); + } } + public static float Clamp(float value, float min, float max) { return value > max ? max : value < min ? min : value; @@ -151,36 +242,92 @@ public static float Clamp(float value, float min, float max) public static T Zero() where T : struct { - return (T)(dynamic)0; + if (typeof(T) == typeof(short)) return (T)(ValueType)(short) 0; + else if (typeof(T) == typeof(int)) return (T)(ValueType)(int) 0; + else if (typeof(T) == typeof(long)) return (T)(ValueType)(long) 0; + else if (typeof(T) == typeof(ushort)) return (T)(ValueType)(ushort) 0; + else if (typeof(T) == typeof(uint)) return (T)(ValueType)(uint) 0; + else if (typeof(T) == typeof(ulong)) return (T)(ValueType)(ulong) 0; + else if (typeof(T) == typeof(byte)) return (T)(ValueType)(byte) 0; + else if (typeof(T) == typeof(sbyte)) return (T)(ValueType)(sbyte) 0; + else if (typeof(T) == typeof(float)) return (T)(ValueType)(float) 0; + else if (typeof(T) == typeof(double)) return (T)(ValueType)(double) 0; + else throw new NotImplementedException(); } public static T One() where T : struct { - return (T)(dynamic)1; + if (typeof(T) == typeof(short)) return (T)(ValueType)(short) 1; + else if (typeof(T) == typeof(int)) return (T)(ValueType)(int) 1; + else if (typeof(T) == typeof(long)) return (T)(ValueType)(long) 1; + else if (typeof(T) == typeof(ushort)) return (T)(ValueType)(ushort) 1; + else if (typeof(T) == typeof(uint)) return (T)(ValueType)(uint) 1; + else if (typeof(T) == typeof(ulong)) return (T)(ValueType)(ulong) 1; + else if (typeof(T) == typeof(byte)) return (T)(ValueType)(byte) 1; + else if (typeof(T) == typeof(sbyte)) return (T)(ValueType)(sbyte) 1; + else if (typeof(T) == typeof(float)) return (T)(ValueType)(float) 1; + else if (typeof(T) == typeof(double)) return (T)(ValueType)(double) 1; + else throw new NotImplementedException(); } public static bool GreaterThan(T left, T right) where T : struct { - var result = (dynamic)left > right; - return (bool)result; + if (left is short) return (short)(ValueType) left > (short)(ValueType) right; + else if (left is int) return (int)(ValueType) left > (int)(ValueType) right; + else if (left is long) return (long)(ValueType) left > (long)(ValueType) right; + else if (left is ushort) return (ushort)(ValueType) left > (ushort)(ValueType) right; + else if (left is uint) return (uint)(ValueType) left > (uint)(ValueType) right; + else if (left is ulong) return (ulong)(ValueType) left > (ulong)(ValueType) right; + else if (left is byte) return (byte)(ValueType) left > (byte)(ValueType) right; + else if (left is sbyte) return (sbyte)(ValueType) left > (sbyte)(ValueType) right; + else if (left is float) return (float)(ValueType) left > (float)(ValueType) right; + else if (left is double) return (double)(ValueType) left > (double)(ValueType) right; + else throw new NotImplementedException(); } public static bool GreaterThanOrEqual(T left, T right) where T : struct { - var result = (dynamic)left >= right; - return (bool)result; + if (left is short) return (short)(ValueType) left >= (short)(ValueType) right; + else if (left is int) return (int)(ValueType) left >= (int)(ValueType) right; + else if (left is long) return (long)(ValueType) left >= (long)(ValueType) right; + else if (left is ushort) return (ushort)(ValueType) left >= (ushort)(ValueType) right; + else if (left is uint) return (uint)(ValueType) left >= (uint)(ValueType) right; + else if (left is ulong) return (ulong)(ValueType) left >= (ulong)(ValueType) right; + else if (left is byte) return (byte)(ValueType) left >= (byte)(ValueType) right; + else if (left is sbyte) return (sbyte)(ValueType) left >= (sbyte)(ValueType) right; + else if (left is float) return (float)(ValueType) left >= (float)(ValueType) right; + else if (left is double) return (double)(ValueType) left >= (double)(ValueType) right; + else throw new NotImplementedException(); } public static bool LessThan(T left, T right) where T : struct { - var result = (dynamic)left < right; - return (bool)result; + if (left is short) return (short)(ValueType) left < (short)(ValueType) right; + else if (left is int) return (int)(ValueType) left < (int)(ValueType) right; + else if (left is long) return (long)(ValueType) left < (long)(ValueType) right; + else if (left is ushort) return (ushort)(ValueType) left < (ushort)(ValueType) right; + else if (left is uint) return (uint)(ValueType) left < (uint)(ValueType) right; + else if (left is ulong) return (ulong)(ValueType) left < (ulong)(ValueType) right; + else if (left is byte) return (byte)(ValueType) left < (byte)(ValueType) right; + else if (left is sbyte) return (sbyte)(ValueType) left < (sbyte)(ValueType) right; + else if (left is float) return (float)(ValueType) left < (float)(ValueType) right; + else if (left is double) return (double)(ValueType) left < (double)(ValueType) right; + else throw new NotImplementedException(); } public static bool LessThanOrEqual(T left, T right) where T : struct { - var result = (dynamic)left <= right; - return (bool)result; + if (left is short) return (short)(ValueType) left <= (short)(ValueType) right; + else if (left is int) return (int)(ValueType) left <= (int)(ValueType) right; + else if (left is long) return (long)(ValueType) left <= (long)(ValueType) right; + else if (left is ushort) return (ushort)(ValueType) left <= (ushort)(ValueType) right; + else if (left is uint) return (uint)(ValueType) left <= (uint)(ValueType) right; + else if (left is ulong) return (ulong)(ValueType) left <= (ulong)(ValueType) right; + else if (left is byte) return (byte)(ValueType) left <= (byte)(ValueType) right; + else if (left is sbyte) return (sbyte)(ValueType) left <= (sbyte)(ValueType) right; + else if (left is float) return (float)(ValueType) left <= (float)(ValueType) right; + else if (left is double) return (double)(ValueType) left <= (double)(ValueType) right; + else throw new NotImplementedException(); } public static bool AnyEqual(T[] left, T[] right) where T : struct From 11adf855e5a5899e53582d9a0a6e7786b41467c3 Mon Sep 17 00:00:00 2001 From: Adeel Mujahid <3840695+am11@users.noreply.github.com> Date: Thu, 14 Oct 2021 00:15:07 +0300 Subject: [PATCH 072/106] Adjust SkipOnCoreClr for Match_ExcessPrefix test (#59564) --- .../System.Text.RegularExpressions/tests/Regex.Match.Tests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Text.RegularExpressions/tests/Regex.Match.Tests.cs b/src/libraries/System.Text.RegularExpressions/tests/Regex.Match.Tests.cs index 5845e04426661..bc4f615b9878f 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/Regex.Match.Tests.cs +++ b/src/libraries/System.Text.RegularExpressions/tests/Regex.Match.Tests.cs @@ -1090,10 +1090,10 @@ public async Task Match_SpecialUnicodeCharacters_Invariant(RegexEngine engine) private static bool IsNotArmProcessAndRemoteExecutorSupported => PlatformDetection.IsNotArmProcess && RemoteExecutor.IsSupported; - [ActiveIssue("https://github.com/dotnet/runtime/issues/59541")] [ConditionalTheory(nameof(IsNotArmProcessAndRemoteExecutorSupported))] // times out on ARM [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, ".NET Framework does not have fix for https://github.com/dotnet/runtime/issues/24749")] - [SkipOnCoreClr("Long running tests: https://github.com/dotnet/runtime/issues/10680", RuntimeConfiguration.Checked, RuntimeTestModes.JitMinOpts)] + [SkipOnCoreClr("Long running tests: https://github.com/dotnet/runtime/issues/10680", RuntimeConfiguration.Checked)] + [SkipOnCoreClr("Long running tests: https://github.com/dotnet/runtime/issues/10680", RuntimeTestModes.JitMinOpts)] [MemberData(nameof(RegexHelpers.AvailableEngines_MemberData), MemberType = typeof(RegexHelpers))] public void Match_ExcessPrefix(RegexEngine engine) { From 51d43eb672fd106dadff8643e835a41fd1bddaf8 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Thu, 14 Oct 2021 01:57:37 +0300 Subject: [PATCH 073/106] JIT: Remove DIV(NEG(a), C) => DIV(a, NEG(C)) opt (#60312) --- src/coreclr/jit/morph.cpp | 18 ------------------ .../JitBlue/Runtime_60297/Runtime_60297.cs | 16 ++++++++++++++++ .../JitBlue/Runtime_60297/Runtime_60297.csproj | 10 ++++++++++ 3 files changed, 26 insertions(+), 18 deletions(-) create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_60297/Runtime_60297.cs create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_60297/Runtime_60297.csproj diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index aba1b5949f391..55ea93a8fba3a 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -11184,24 +11184,6 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac) return fgMorphSmpOp(tree, mac); } - if (opts.OptimizationEnabled() && !optValnumCSE_phase) - { - // DIV(NEG(a), C) => DIV(a, NEG(C)) - if (op1->OperIs(GT_NEG) && !op1->gtGetOp1()->IsCnsIntOrI() && op2->IsCnsIntOrI() && - !op2->IsIconHandle()) - { - ssize_t op2Value = op2->AsIntCon()->IconValue(); - if (op2Value != 1 && op2Value != -1) // Div must throw exception for int(long).MinValue / -1. - { - tree->AsOp()->gtOp1 = op1->gtGetOp1(); - DEBUG_DESTROY_NODE(op1); - tree->AsOp()->gtOp2 = gtNewIconNode(-op2Value, op2->TypeGet()); - DEBUG_DESTROY_NODE(op2); - return fgMorphSmpOp(tree, mac); - } - } - } - #ifndef TARGET_64BIT if (typ == TYP_LONG) { diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_60297/Runtime_60297.cs b/src/tests/JIT/Regression/JitBlue/Runtime_60297/Runtime_60297.cs new file mode 100644 index 0000000000000..fdddd25461345 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_60297/Runtime_60297.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. + +// Generated by Fuzzlyn v1.5 on 2021-10-12 17:42:07 +// Run on .NET 6.0.0-rc.1.21451.13 on X64 Windows +// Seed: 4133580165890247722 + +using System.Runtime.CompilerServices; + +class Program +{ + static int Main() => Test(31) == -65538 ? 100 : 0; + + [MethodImpl(MethodImplOptions.NoInlining)] + static int Test(int x) => -(1 << x) / 32767; +} \ No newline at end of file diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_60297/Runtime_60297.csproj b/src/tests/JIT/Regression/JitBlue/Runtime_60297/Runtime_60297.csproj new file mode 100644 index 0000000000000..1100f420532dc --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_60297/Runtime_60297.csproj @@ -0,0 +1,10 @@ + + + Exe + None + True + + + + + From 56b9b822585aa0b88eb5c671683c5337fdc17214 Mon Sep 17 00:00:00 2001 From: Juan Hoyos <19413848+hoyosjs@users.noreply.github.com> Date: Wed, 13 Oct 2021 19:05:02 -0700 Subject: [PATCH 074/106] Fix StartTLS in DirectoryService.Protocols in 6.0 and backport changes from 5.0 release. (#60359) --- .../Interop/Linux/OpenLdap/Interop.Ldap.cs | 3 +- .../System.DirectoryServices.Protocols.csproj | 4 +++ .../Protocols/Interop/LdapPal.Linux.cs | 26 +++++++++++++- .../Protocols/Interop/SafeHandles.Linux.cs | 7 ++++ .../Protocols/ldap/LdapConnection.Linux.cs | 34 ++++++++++++++++--- .../Protocols/ldap/LdapSessionOptions.cs | 5 ++- .../Protocols/ldap/LocalAppContextSwitches.cs | 18 ++++++++++ 7 files changed, 90 insertions(+), 7 deletions(-) create mode 100644 src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LocalAppContextSwitches.cs diff --git a/src/libraries/Common/src/Interop/Linux/OpenLdap/Interop.Ldap.cs b/src/libraries/Common/src/Interop/Linux/OpenLdap/Interop.Ldap.cs index f38bb375b347e..7fbaf9cc32eab 100644 --- a/src/libraries/Common/src/Interop/Linux/OpenLdap/Interop.Ldap.cs +++ b/src/libraries/Common/src/Interop/Linux/OpenLdap/Interop.Ldap.cs @@ -136,8 +136,9 @@ static Ldap() [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_set_option", CharSet = CharSet.Ansi)] public static extern int ldap_set_option_referral([In] ConnectionHandle ldapHandle, [In] LdapOption option, ref LdapReferralCallback outValue); + // Note that ldap_start_tls_s has a different signature across Windows LDAP and OpenLDAP [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_start_tls_s", CharSet = CharSet.Ansi)] - public static extern int ldap_start_tls(ConnectionHandle ldapHandle, ref int ServerReturnValue, ref IntPtr Message, IntPtr ServerControls, IntPtr ClientControls); + public static extern int ldap_start_tls(ConnectionHandle ldapHandle, IntPtr serverControls, IntPtr clientControls); [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_parse_result", CharSet = CharSet.Ansi)] public static extern int ldap_parse_result([In] ConnectionHandle ldapHandle, [In] IntPtr result, ref int serverError, ref IntPtr dn, ref IntPtr message, ref IntPtr referral, ref IntPtr control, byte freeIt); diff --git a/src/libraries/System.DirectoryServices.Protocols/src/System.DirectoryServices.Protocols.csproj b/src/libraries/System.DirectoryServices.Protocols/src/System.DirectoryServices.Protocols.csproj index 108bf2f43b9f2..8ee73ae4c1d28 100644 --- a/src/libraries/System.DirectoryServices.Protocols/src/System.DirectoryServices.Protocols.csproj +++ b/src/libraries/System.DirectoryServices.Protocols/src/System.DirectoryServices.Protocols.csproj @@ -67,7 +67,11 @@ + + + Common\System\LocalAppContextSwitches.Common.cs + Common\Interop\Linux\OpenLdap\Interop.Ldap.cs diff --git a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/LdapPal.Linux.cs b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/LdapPal.Linux.cs index 701fae2610828..d67782f40ad36 100644 --- a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/LdapPal.Linux.cs +++ b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/LdapPal.Linux.cs @@ -123,7 +123,31 @@ internal static int BindToDirectory(ConnectionHandle ld, string who, string pass } } - internal static int StartTls(ConnectionHandle ldapHandle, ref int ServerReturnValue, ref IntPtr Message, IntPtr ServerControls, IntPtr ClientControls) => Interop.Ldap.ldap_start_tls(ldapHandle, ref ServerReturnValue, ref Message, ServerControls, ClientControls); + internal static int StartTls(ConnectionHandle ldapHandle, ref int serverReturnValue, ref IntPtr message, IntPtr serverControls, IntPtr clientControls) + { + // Windows and Linux have different signatures for ldap_start_tls_s. + // On Linux, we don't have a serverReturnValue or the message/result parameter. + // + // So in the PAL here, just emulate. + + int error = Interop.Ldap.ldap_start_tls(ldapHandle, serverControls, clientControls); + + // On Windows, serverReturnValue only has meaning if the result code is LDAP_OTHER. + // If OpenLDAP returns that, we don't have a better code, so assign that through. + // If we get any other error, assign serverReturnValue to 0 since it shouldn't be read. + if (error == (int)ResultCode.Other) + { + serverReturnValue = error; + } + else + { + serverReturnValue = 0; + } + + // We don't have a referrer/message/result value, so just set it to NULL. + message = IntPtr.Zero; + return error; + } // openldap doesn't have a ldap_stop_tls function. Returning true as no-op for Linux. internal static byte StopTls(ConnectionHandle ldapHandle) => 1; diff --git a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/SafeHandles.Linux.cs b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/SafeHandles.Linux.cs index 2383e816e1ddb..a7e43947e2031 100644 --- a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/SafeHandles.Linux.cs +++ b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/SafeHandles.Linux.cs @@ -16,6 +16,13 @@ public ConnectionHandle() _needDispose = true; } + internal ConnectionHandle(string uri) + :base(true) + { + Interop.Ldap.ldap_initialize(out handle, uri); + _needDispose = true; + } + internal ConnectionHandle(IntPtr value, bool disposeHandle) : base(true) { _needDispose = disposeHandle; diff --git a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapConnection.Linux.cs b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapConnection.Linux.cs index 02c8f0c239587..6adf1ebac982c 100644 --- a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapConnection.Linux.cs +++ b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapConnection.Linux.cs @@ -20,7 +20,7 @@ private void InternalInitConnectionHandle(string hostname) throw new NullReferenceException(); } - _ldapHandle = new ConnectionHandle(); + _ldapHandle = new ConnectionHandle($"ldap://{hostname}:{((LdapDirectoryIdentifier)_directoryIdentifier).PortNumber}"); } private int InternalConnectToServer() @@ -79,13 +79,39 @@ private int InternalConnectToServer() private int InternalBind(NetworkCredential tempCredential, SEC_WINNT_AUTH_IDENTITY_EX cred, BindMethod method) { int error; - if (tempCredential == null && (AuthType == AuthType.External || AuthType == AuthType.Kerberos)) + + if (LocalAppContextSwitches.UseBasicAuthFallback) { - error = BindSasl(); + if (tempCredential == null && (AuthType == AuthType.External || AuthType == AuthType.Kerberos)) + { + error = BindSasl(); + } + else + { + error = LdapPal.BindToDirectory(_ldapHandle, cred.user, cred.password); + } } else { - error = LdapPal.BindToDirectory(_ldapHandle, cred.user, cred.password); + if (method == BindMethod.LDAP_AUTH_NEGOTIATE) + { + if (tempCredential == null) + { + error = BindSasl(); + } + else + { + // Explicit credentials were provided. If we call ldap_bind_s it will + // return LDAP_NOT_SUPPORTED, so just skip the P/Invoke. + error = (int)LdapError.NotSupported; + } + } + else + { + // Basic and Anonymous are handled elsewhere. + Debug.Assert(AuthType != AuthType.Anonymous && AuthType != AuthType.Basic); + error = (int)LdapError.AuthUnknown; + } } return error; diff --git a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapSessionOptions.cs b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapSessionOptions.cs index 5b28020eed2fd..9a200fc5ba218 100644 --- a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapSessionOptions.cs +++ b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapSessionOptions.cs @@ -641,11 +641,14 @@ public unsafe void StartTransportLayerSecurity(DirectoryControlCollection contro response.ResponseName = "1.3.6.1.4.1.1466.20037"; throw new TlsOperationException(response); } - else if (LdapErrorMappings.IsLdapError(error)) + + if (LdapErrorMappings.IsLdapError(error)) { string errorMessage = LdapErrorMappings.MapResultCode(error); throw new LdapException(error, errorMessage); } + + throw new LdapException(error); } } finally diff --git a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LocalAppContextSwitches.cs b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LocalAppContextSwitches.cs new file mode 100644 index 0000000000000..59ddfdf7f10ed --- /dev/null +++ b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LocalAppContextSwitches.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 System.Runtime.CompilerServices; + +namespace System +{ + internal static partial class LocalAppContextSwitches + { + private static int s_useBasicAuthFallback; + + public static bool UseBasicAuthFallback + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => GetCachedSwitchValue("System.DirectoryServices.Protocols.UseBasicAuthFallback", ref s_useBasicAuthFallback); + } + } +} From a546fa486cedbabe3d9cf9fbf25debf7e7578698 Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Thu, 14 Oct 2021 06:00:56 +0200 Subject: [PATCH 075/106] [wasm] rationalize internal exports (#60075) - introduce globalThis.INTERNAL and move all exported methods which we only use internally or for testing - reduce BINDING and MONO exports to minimal scope necessary - as used by Blazor - fix all internal usages in tests - produce dotnet.d.ts and include it in the workload - moved Module.config to MONO.config - added mono_load_runtime_and_bcl_args into MONO export - removed obsolete debugger test InvalidScopeId - introduced INTERNAL.mono_wasm_set_main_args - add mono_wasm_new_root_buffer, mono_wasm_new_root, mono_wasm_release_roots to the MONO interface --- .../Directory.Build.props | 1 + .../src/Interop/Browser/Interop.Runtime.cs | 2 +- .../Native/Unix/System.Native/pal_random.js | 8 +- .../JavaScript/MarshalTests.cs | 46 +- src/mono/mono/component/mini-wasm-debugger.c | 10 +- src/mono/sample/mbr/browser/index.html | 6 +- src/mono/sample/mbr/browser/runtime.js | 20 +- src/mono/sample/wasm/browser-bench/index.html | 4 +- src/mono/sample/wasm/browser-bench/runtime.js | 30 +- .../sample/wasm/browser-profile/README.md | 8 +- .../sample/wasm/browser-profile/runtime.js | 65 +- src/mono/sample/wasm/browser/index.html | 2 +- src/mono/sample/wasm/browser/runtime.js | 23 +- .../BrowserDebugProxy/DevToolsHelper.cs | 22 +- .../DebuggerTestSuite/BreakpointTests.cs | 12 +- .../DebuggerTestSuite/DebuggerTestBase.cs | 7 +- .../debugger/DebuggerTestSuite/MonoJsTests.cs | 38 +- .../tests/debugger-test/debugger-driver.html | 20 +- .../debugger/tests/debugger-test/other.js | 10 +- .../tests/debugger-test/runtime-debugger.js | 30 +- src/mono/wasm/runtime-test.js | 638 +++++++++--------- src/mono/wasm/runtime/CMakeLists.txt | 2 +- src/mono/wasm/runtime/buffers.ts | 2 +- src/mono/wasm/runtime/cancelable-promise.ts | 2 +- src/mono/wasm/runtime/corebindings.c | 2 +- src/mono/wasm/runtime/cs-to-js.ts | 2 +- src/mono/wasm/runtime/cwraps.ts | 17 +- src/mono/wasm/runtime/debug.ts | 1 + src/mono/wasm/runtime/driver.c | 21 +- src/mono/wasm/runtime/exports.ts | 167 +++-- src/mono/wasm/runtime/icu.ts | 2 +- src/mono/wasm/runtime/js-to-cs.ts | 2 +- src/mono/wasm/runtime/library-dotnet.js | 33 +- src/mono/wasm/runtime/method-binding.ts | 2 +- src/mono/wasm/runtime/method-calls.ts | 2 +- src/mono/wasm/runtime/modules.ts | 22 +- src/mono/wasm/runtime/package-lock.json | 25 + src/mono/wasm/runtime/package.json | 7 +- src/mono/wasm/runtime/profiler.ts | 4 +- src/mono/wasm/runtime/rollup.config.js | 41 +- src/mono/wasm/runtime/roots.ts | 1 + src/mono/wasm/runtime/startup.ts | 28 +- src/mono/wasm/runtime/strings.ts | 2 +- src/mono/wasm/runtime/tsconfig.json | 1 - src/mono/wasm/runtime/types.ts | 44 +- src/mono/wasm/runtime/types/emscripten.d.ts | 8 +- src/mono/wasm/runtime/web-socket.ts | 2 +- src/mono/wasm/wasm.proj | 2 +- .../WebAssembly/Browser/HotReload/index.html | 2 +- .../WebAssembly/Browser/HotReload/runtime.js | 33 +- .../Browser/RuntimeConfig/index.html | 2 +- .../Browser/RuntimeConfig/runtime.js | 27 +- 52 files changed, 783 insertions(+), 727 deletions(-) diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props index 3d070f437073b..12faf8e6f3f5f 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props @@ -218,6 +218,7 @@ + diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs index d6ac21cf3219f..051b412b1199d 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs @@ -98,7 +98,7 @@ public static void StopProfile() { } - // Called by the AOT profiler to save profile data into Module.aot_profile_data + // Called by the AOT profiler to save profile data into INTERNAL.aot_profile_data [MethodImplAttribute(MethodImplOptions.NoInlining)] public static unsafe void DumpAotProfileData(ref byte buf, int len, string extraArg) { diff --git a/src/libraries/Native/Unix/System.Native/pal_random.js b/src/libraries/Native/Unix/System.Native/pal_random.js index 8e7c7af05f131..c0bbc9c9f0f9e 100644 --- a/src/libraries/Native/Unix/System.Native/pal_random.js +++ b/src/libraries/Native/Unix/System.Native/pal_random.js @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -var DotNetEntropyLib = { +const DotNetEntropyLib = { $DOTNETENTROPY: { // batchedQuotaMax is the max number of bytes as specified by the api spec. // If the byteLength of array is greater than 65536, throw a QuotaExceededError and terminate the algorithm. @@ -10,13 +10,13 @@ var DotNetEntropyLib = { getBatchedRandomValues: function (buffer, bufferLength) { // for modern web browsers // map the work array to the memory buffer passed with the length - for (var i = 0; i < bufferLength; i += this.batchedQuotaMax) { - var view = new Uint8Array(Module.HEAPU8.buffer, buffer + i, Math.min(bufferLength - i, this.batchedQuotaMax)); + for (let i = 0; i < bufferLength; i += this.batchedQuotaMax) { + const view = new Uint8Array(Module.HEAPU8.buffer, buffer + i, Math.min(bufferLength - i, this.batchedQuotaMax)); crypto.getRandomValues(view) } } }, - dotnet_browser_entropy : function (buffer, bufferLength) { + dotnet_browser_entropy: function (buffer, bufferLength) { // check that we have crypto available if (typeof crypto === 'object' && typeof crypto['getRandomValues'] === 'function') { DOTNETENTROPY.getBatchedRandomValues(buffer, bufferLength) diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/MarshalTests.cs b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/MarshalTests.cs index b70e07511c485..101ce90eaa272 100644 --- a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/MarshalTests.cs +++ b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/MarshalTests.cs @@ -283,7 +283,7 @@ public static void BindStaticMethod() { HelperMarshal._intValue = 0; Runtime.InvokeJS(@$" - var invoke_int = Module.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeInt""); + var invoke_int = INTERNAL.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeInt""); invoke_int (200); "); @@ -295,7 +295,7 @@ public static void BindIntPtrStaticMethod() { HelperMarshal._intPtrValue = IntPtr.Zero; Runtime.InvokeJS(@$" - var invoke_int_ptr = Module.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeIntPtr""); + var invoke_int_ptr = INTERNAL.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeIntPtr""); invoke_int_ptr (42); "); Assert.Equal(42, (int)HelperMarshal._intPtrValue); @@ -306,7 +306,7 @@ public static void MarshalIntPtrToJS() { HelperMarshal._marshaledIntPtrValue = IntPtr.Zero; Runtime.InvokeJS(@$" - var invokeMarshalIntPtr = Module.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeMarshalIntPtr""); + var invokeMarshalIntPtr = INTERNAL.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeMarshalIntPtr""); var r = invokeMarshalIntPtr (); if (r != 42) throw `Invalid int_ptr value`; @@ -319,7 +319,7 @@ public static void InvokeStaticMethod() { HelperMarshal._intValue = 0; Runtime.InvokeJS(@$" - Module.mono_call_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeInt"", [ 300 ]); + INTERNAL.call_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeInt"", [ 300 ]); "); Assert.Equal(300, HelperMarshal._intValue); @@ -330,7 +330,7 @@ public static void ResolveMethod() { HelperMarshal._intValue = 0; Runtime.InvokeJS(@$" - var invoke_int = Module.mono_method_resolve (""{HelperMarshal.INTEROP_CLASS}InvokeInt""); + var invoke_int = INTERNAL.mono_method_resolve (""{HelperMarshal.INTEROP_CLASS}InvokeInt""); App.call_test_method (""InvokeInt"", [ invoke_int ]); "); @@ -629,7 +629,7 @@ public static void BoundStaticMethodMissingArgs() HelperMarshal._intValue = 1; Runtime.InvokeJS(@$" - var invoke_int = Module.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeInt""); + var invoke_int = INTERNAL.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeInt""); invoke_int (); "); Assert.Equal(0, HelperMarshal._intValue); @@ -640,7 +640,7 @@ public static void BoundStaticMethodExtraArgs() { HelperMarshal._intValue = 0; Runtime.InvokeJS(@$" - var invoke_int = Module.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeInt""); + var invoke_int = INTERNAL.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeInt""); invoke_int (200, 400); "); Assert.Equal(200, HelperMarshal._intValue); @@ -654,13 +654,13 @@ public static void BoundStaticMethodArgumentTypeCoercion() HelperMarshal._intValue = 0; Runtime.InvokeJS(@$" - var invoke_int = Module.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeInt""); + var invoke_int = INTERNAL.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeInt""); invoke_int (""200""); "); Assert.Equal(200, HelperMarshal._intValue); Runtime.InvokeJS(@$" - var invoke_int = Module.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeInt""); + var invoke_int = INTERNAL.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeInt""); invoke_int (400.5); "); Assert.Equal(400, HelperMarshal._intValue); @@ -671,14 +671,14 @@ public static void BoundStaticMethodUnpleasantArgumentTypeCoercion() { HelperMarshal._intValue = 100; Runtime.InvokeJS(@$" - var invoke_int = Module.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeInt""); + var invoke_int = INTERNAL.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeInt""); invoke_int (""hello""); "); Assert.Equal(0, HelperMarshal._intValue); // In this case at the very least, the leading "7" is not turned into the number 7 Runtime.InvokeJS(@$" - var invoke_int = Module.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeInt""); + var invoke_int = INTERNAL.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeInt""); invoke_int (""7apples""); "); Assert.Equal(0, HelperMarshal._intValue); @@ -689,7 +689,7 @@ public static void PassUintArgument() { HelperMarshal._uintValue = 0; Runtime.InvokeJS(@$" - var invoke_uint = Module.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeUInt""); + var invoke_uint = INTERNAL.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeUInt""); invoke_uint (0xFFFFFFFE); "); @@ -702,9 +702,9 @@ public static void ReturnUintEnum () HelperMarshal._uintValue = 0; HelperMarshal._enumValue = TestEnum.BigValue; Runtime.InvokeJS(@$" - var get_value = Module.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}GetEnumValue""); + var get_value = INTERNAL.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}GetEnumValue""); var e = get_value (); - var invoke_uint = Module.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeUInt""); + var invoke_uint = INTERNAL.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeUInt""); invoke_uint (e); "); Assert.Equal((uint)TestEnum.BigValue, HelperMarshal._uintValue); @@ -715,7 +715,7 @@ public static void PassUintEnumByValue () { HelperMarshal._enumValue = TestEnum.Zero; Runtime.InvokeJS(@$" - var set_enum = Module.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}SetEnumValue"", ""j""); + var set_enum = INTERNAL.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}SetEnumValue"", ""j""); set_enum (0xFFFFFFFE); "); Assert.Equal(TestEnum.BigValue, HelperMarshal._enumValue); @@ -728,7 +728,7 @@ public static void PassUintEnumByValueMasqueradingAsInt () // HACK: We're explicitly telling the bindings layer to pass an int here, not an enum // Because we know the enum is : uint, this is compatible, so it works. Runtime.InvokeJS(@$" - var set_enum = Module.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}SetEnumValue"", ""i""); + var set_enum = INTERNAL.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}SetEnumValue"", ""i""); set_enum (0xFFFFFFFE); "); Assert.Equal(TestEnum.BigValue, HelperMarshal._enumValue); @@ -740,7 +740,7 @@ public static void PassUintEnumByNameIsNotImplemented () HelperMarshal._enumValue = TestEnum.Zero; var exc = Assert.Throws( () => Runtime.InvokeJS(@$" - var set_enum = Module.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}SetEnumValue"", ""j""); + var set_enum = INTERNAL.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}SetEnumValue"", ""j""); set_enum (""BigValue""); ") ); @@ -752,7 +752,7 @@ public static void CannotUnboxUint64 () { var exc = Assert.Throws( () => Runtime.InvokeJS(@$" - var get_u64 = Module.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}GetUInt64"", """"); + var get_u64 = INTERNAL.mono_bind_static_method (""{HelperMarshal.INTEROP_CLASS}GetUInt64"", """"); var u64 = get_u64(); ") ); @@ -806,7 +806,7 @@ public static void ManuallyInternString() { HelperMarshal._stringResource = HelperMarshal._stringResource2 = null; Runtime.InvokeJS(@" - var sym = BINDING.mono_intern_string(""interned string 3""); + var sym = INTERNAL.mono_intern_string(""interned string 3""); App.call_test_method (""InvokeString"", [ sym ], ""s""); App.call_test_method (""InvokeString2"", [ sym ], ""s""); "); @@ -823,7 +823,7 @@ public static void LargeStringsAreNotAutomaticallyLocatedInInternTable() var s = ""long interned string""; for (var i = 0; i < 1024; i++) s += String(i % 10); - var sym = BINDING.mono_intern_string(s); + var sym = INTERNAL.mono_intern_string(s); App.call_test_method (""InvokeString"", [ sym ], ""S""); App.call_test_method (""InvokeString2"", [ sym ], ""s""); "); @@ -837,7 +837,7 @@ public static void CanInternVeryManyStrings() HelperMarshal._stringResource = null; Runtime.InvokeJS(@" for (var i = 0; i < 10240; i++) - BINDING.mono_intern_string('s' + i); + INTERNAL.mono_intern_string('s' + i); App.call_test_method (""InvokeString"", [ 's5000' ], ""S""); "); Assert.Equal("s5000", HelperMarshal._stringResource); @@ -864,8 +864,8 @@ public static void InternedStringReturnValuesWork() HelperMarshal._stringResource = HelperMarshal._stringResource2 = null; var fqn = "[System.Private.Runtime.InteropServices.JavaScript.Tests]System.Runtime.InteropServices.JavaScript.Tests.HelperMarshal:StoreArgumentAndReturnLiteral"; Runtime.InvokeJS( - $"var a = BINDING.bind_static_method('{fqn}')('test');\r\n" + - $"var b = BINDING.bind_static_method('{fqn}')(a);\r\n" + + $"var a = INTERNAL.mono_bind_static_method('{fqn}')('test');\r\n" + + $"var b = INTERNAL.mono_bind_static_method('{fqn}')(a);\r\n" + "App.call_test_method ('InvokeString2', [ b ]);" ); Assert.Equal("s: 1 length: 1", HelperMarshal._stringResource); diff --git a/src/mono/mono/component/mini-wasm-debugger.c b/src/mono/mono/component/mini-wasm-debugger.c index f70738d7cb0c1..77ceed873a245 100644 --- a/src/mono/mono/component/mini-wasm-debugger.c +++ b/src/mono/mono/component/mini-wasm-debugger.c @@ -68,8 +68,8 @@ void wasm_debugger_log (int level, const gchar *format, ...) var message = Module.UTF8ToString ($1); var namespace = "Debugger.Debug"; - if (MONO["logging"] && MONO.logging["debugger"]) { - MONO.logging.debugger (level, message); + if (INTERNAL["logging"] && INTERNAL.logging["debugger"]) { + INTERNAL.logging.debugger (level, message); return; } @@ -375,7 +375,7 @@ mono_wasm_send_dbg_command_with_parms (int id, MdbgProtCommandSet command_set, i 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); + INTERNAL.mono_wasm_add_dbg_command_received ($0, $1, $2, $3); }, 0, id, 0, 0); return TRUE; } @@ -403,7 +403,7 @@ mono_wasm_send_dbg_command (int id, MdbgProtCommandSet command_set, int command, 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); + INTERNAL.mono_wasm_add_dbg_command_received ($0, $1, $2, $3); }, error == MDBGPROT_ERR_NONE, id, buf.buf, buf.p-buf.buf); buffer_free (&buf); @@ -414,7 +414,7 @@ static gboolean receive_debugger_agent_message (void *data, int len) { EM_ASM ({ - MONO.mono_wasm_add_dbg_command_received (1, -1, $0, $1); + INTERNAL.mono_wasm_add_dbg_command_received (1, -1, $0, $1); }, data, len); mono_wasm_save_thread_context(); mono_wasm_fire_debugger_agent_message (); diff --git a/src/mono/sample/mbr/browser/index.html b/src/mono/sample/mbr/browser/index.html index eb19b7bb2f595..6122370dd9d65 100644 --- a/src/mono/sample/mbr/browser/index.html +++ b/src/mono/sample/mbr/browser/index.html @@ -18,12 +18,12 @@ init: function () { var outElement = document.getElementById("out"); document.getElementById("update").addEventListener("click", function () { - BINDING.call_static_method("[WasmDelta] Sample.Test:Update", []); + INTERNAL.call_static_method("[WasmDelta] Sample.Test:Update", []); console.log ("applied update"); - var ret = BINDING.call_static_method("[WasmDelta] Sample.Test:TestMeaning", []); + var ret = INTERNAL.call_static_method("[WasmDelta] Sample.Test:TestMeaning", []); outElement.innerHTML = ret; }) - var ret = BINDING.call_static_method("[WasmDelta] Sample.Test:TestMeaning", []); + var ret = INTERNAL.call_static_method("[WasmDelta] Sample.Test:TestMeaning", []); outElement.innerHTML = ret; console.log ("ready"); }, diff --git a/src/mono/sample/mbr/browser/runtime.js b/src/mono/sample/mbr/browser/runtime.js index 41989feb6a547..0252041eff5ac 100644 --- a/src/mono/sample/mbr/browser/runtime.js +++ b/src/mono/sample/mbr/browser/runtime.js @@ -2,30 +2,30 @@ // The .NET Foundation licenses this file to you under the MIT license. "use strict"; -var Module = { +var Module = { config: null, - preInit: async function() { - await MONO.mono_wasm_load_config("./mono-config.json"); // sets Module.config implicitly + preInit: async function () { + await MONO.mono_wasm_load_config("./mono-config.json"); // sets MONO.config implicitly }, // Called when the runtime is initialized and wasm is ready onRuntimeInitialized: function () { - if (!Module.config || Module.config.error) { + if (!MONO.config || MONO.config.error) { console.log("An error occured while loading the config file"); return; } - Module.config.loaded_cb = function () { - App.init (); + MONO.config.loaded_cb = function () { + App.init(); }; - Module.config.environment_variables = { + MONO.config.environment_variables = { "DOTNET_MODIFIABLE_ASSEMBLIES": "debug" }; - Module.config.fetch_file_cb = function (asset) { - return fetch (asset, { credentials: 'same-origin' }); + MONO.config.fetch_file_cb = function (asset) { + return fetch(asset, { credentials: 'same-origin' }); } - MONO.mono_load_runtime_and_bcl_args (Module.config); + MONO.mono_load_runtime_and_bcl_args(MONO.config); }, }; diff --git a/src/mono/sample/wasm/browser-bench/index.html b/src/mono/sample/wasm/browser-bench/index.html index 6b26090c6dddf..136e400e70c2a 100644 --- a/src/mono/sample/wasm/browser-bench/index.html +++ b/src/mono/sample/wasm/browser-bench/index.html @@ -37,7 +37,7 @@ }; function yieldBench () { - let promise = BINDING.call_static_method("[Wasm.Browser.Bench.Sample] Sample.Test:RunBenchmark", []); + let promise = INTERNAL.call_static_method("[Wasm.Browser.Bench.Sample] Sample.Test:RunBenchmark", []); promise.then(ret => { document.getElementById("out").innerHTML += ret; if (ret.length > 0) { @@ -52,7 +52,7 @@ var App = { init: function () { if (tasks != '') - BINDING.call_static_method("[Wasm.Browser.Bench.Sample] Sample.Test:SetTasks", tasks); + INTERNAL.call_static_method("[Wasm.Browser.Bench.Sample] Sample.Test:SetTasks", tasks); yieldBench (); } }; diff --git a/src/mono/sample/wasm/browser-bench/runtime.js b/src/mono/sample/wasm/browser-bench/runtime.js index 87fb6436dbc1d..2d934c659ea13 100644 --- a/src/mono/sample/wasm/browser-bench/runtime.js +++ b/src/mono/sample/wasm/browser-bench/runtime.js @@ -2,46 +2,44 @@ // The .NET Foundation licenses this file to you under the MIT license. "use strict"; -var Module = { +var Module = { config: null, - preInit: async function() { - await MONO.mono_wasm_load_config("./mono-config.json"); // sets Module.config implicitly + preInit: async function () { + await MONO.mono_wasm_load_config("./mono-config.json"); // sets MONO.config implicitly }, // Called when the runtime is initialized and wasm is ready onRuntimeInitialized: function () { - if (!Module.config || Module.config.error) { + if (!MONO.config || MONO.config.error) { console.log("An error occured while loading the config file"); return; } - Module.config.loaded_cb = function () { + MONO.config.loaded_cb = function () { try { - App.init (); + App.init(); } catch (error) { test_exit(1); throw (error); } }; - Module.config.fetch_file_cb = function (asset) { - return fetch (asset, { credentials: 'same-origin' }); + MONO.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", + if (MONO.config.enable_profiler) { + MONO.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 (Module.config); + try { + MONO.mono_load_runtime_and_bcl_args(MONO.config); } catch (error) { test_exit(1); - throw(error); + throw (error); } }, }; diff --git a/src/mono/sample/wasm/browser-profile/README.md b/src/mono/sample/wasm/browser-profile/README.md index 1e2981be112f7..26afb1386711b 100644 --- a/src/mono/sample/wasm/browser-profile/README.md +++ b/src/mono/sample/wasm/browser-profile/README.md @@ -27,16 +27,16 @@ var Module = { 3. Call the `write_at` method at the end of the app, either in C# or in JS. To call the `write_at` method in JS, make use of bindings: -`BINDING.call_static_method("<[ProjectName] Namespace.Class::StopProfile">, []);` +`INTERNAL.call_static_method("<[ProjectName] Namespace.Class::StopProfile">, []);` -When the `write_at` method is called, the `send_to` method `DumpAotProfileData` stores the profile data into `Module.aot_profile_data` +When the `write_at` method is called, the `send_to` method `DumpAotProfileData` stores the profile data into `INTERNAL.aot_profile_data` -4. Download `Module.aot_profile_data` in JS, using something similar to: +4. Download `INTERNAL.aot_profile_data` in JS, using something similar to: ``` function saveProfile() { var a = document.createElement('a'); - var blob = new Blob([Module.aot_profile_data]); + var blob = new Blob([INTERNAL.aot_profile_data]); a.href = URL.createObjectURL(blob); a.download = "data.aotprofile"; // Append anchor to body. diff --git a/src/mono/sample/wasm/browser-profile/runtime.js b/src/mono/sample/wasm/browser-profile/runtime.js index cd0dd0cb9ed32..69b84d002bb8d 100644 --- a/src/mono/sample/wasm/browser-profile/runtime.js +++ b/src/mono/sample/wasm/browser-profile/runtime.js @@ -2,22 +2,22 @@ // The .NET Foundation licenses this file to you under the MIT license. "use strict"; -var Module = { +var Module = { is_testing: false, config: null, - preInit: async function() { - await MONO.mono_wasm_load_config("./mono-config.json"); // sets Module.config implicitly + preInit: async function () { + await MONO.mono_wasm_load_config("./mono-config.json"); // sets MONO.config implicitly }, // Called when the runtime is initialized and wasm is ready onRuntimeInitialized: function () { - if (!Module.config || Module.config.error) { + if (!MONO.config || MONO.config.error) { console.log("An error occured while loading the config file"); return; } - Module.config.loaded_cb = function () { + MONO.config.loaded_cb = function () { try { Module.init(); } catch (error) { @@ -25,70 +25,67 @@ var Module = { throw (error); } }; - Module.config.fetch_file_cb = function (asset) { - return fetch (asset, { credentials: 'same-origin' }); + MONO.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", + if (MONO.config.enable_profiler) { + MONO.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 (Module.config); + try { + MONO.mono_load_runtime_and_bcl_args(MONO.config); } catch (error) { Module.test_exit(1); - throw(error); + throw (error); } }, init: function () { console.log("not ready yet") - var ret = BINDING.call_static_method("[Wasm.BrowserProfile.Sample] Sample.Test:TestMeaning", []); + const ret = INTERNAL.call_static_method("[Wasm.BrowserProfile.Sample] Sample.Test:TestMeaning", []); document.getElementById("out").innerHTML = ret; - console.log ("ready"); + 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.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(); + if (MONO.config.enable_profiler) { + INTERNAL.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'); + onLoad: function () { + const url = new URL(decodeURI(window.location)); + const args = url.searchParams.getAll('arg'); Module.is_testing = args !== undefined && (args.find(arg => arg == '--testing') !== undefined); }, - - test_exit: function(exit_code) { + + 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"); + const 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]); + const a = document.createElement('a'); + const blob = new Blob([INTERNAL.aot_profile_data]); a.href = URL.createObjectURL(blob); a.download = "data.aotprofile"; // Append anchor to body. diff --git a/src/mono/sample/wasm/browser/index.html b/src/mono/sample/wasm/browser/index.html index 5f170bb38e7bc..cedcae5715afb 100644 --- a/src/mono/sample/wasm/browser/index.html +++ b/src/mono/sample/wasm/browser/index.html @@ -36,7 +36,7 @@ var App = { init: function () { - var ret = BINDING.call_static_method("[Wasm.Browser.Sample] Sample.Test:TestMeaning", []); + var ret = INTERNAL.call_static_method("[Wasm.Browser.Sample] Sample.Test:TestMeaning", []); document.getElementById("out").innerHTML = ret; if (is_testing) diff --git a/src/mono/sample/wasm/browser/runtime.js b/src/mono/sample/wasm/browser/runtime.js index 904ca19958f76..8b14c3637e6b4 100644 --- a/src/mono/sample/wasm/browser/runtime.js +++ b/src/mono/sample/wasm/browser/runtime.js @@ -2,39 +2,38 @@ // The .NET Foundation licenses this file to you under the MIT license. "use strict"; -var Module = { +var Module = { config: null, - preInit: async function() { - await MONO.mono_wasm_load_config("./mono-config.json"); // sets Module.config implicitly + preInit: async function () { + await MONO.mono_wasm_load_config("./mono-config.json"); // sets MONO.config implicitly }, // Called when the runtime is initialized and wasm is ready onRuntimeInitialized: function () { - if (!Module.config || Module.config.error) { + if (!MONO.config || MONO.config.error) { console.log("No config found"); return; } - Module.config.loaded_cb = function () { + MONO.config.loaded_cb = function () { try { - App.init (); + App.init(); } catch (error) { test_exit(1); throw (error); } }; - Module.config.fetch_file_cb = function (asset) { - return fetch (asset, { credentials: 'same-origin' }); + MONO.config.fetch_file_cb = function (asset) { + return fetch(asset, { credentials: 'same-origin' }); } - try - { - MONO.mono_load_runtime_and_bcl_args (Module.config); + try { + MONO.mono_load_runtime_and_bcl_args(MONO.config); } catch (error) { test_exit(1); - throw(error); + throw (error); } }, }; diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs index 950acbb2dcf91..97acfe9517d4e 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs @@ -172,33 +172,31 @@ internal class MonoCommands public MonoCommands(string expression) => this.expression = expression; - public static MonoCommands GetExceptionObject() => new MonoCommands("MONO.mono_wasm_get_exception_object()"); + public static MonoCommands GetDebuggerAgentBufferReceived() => new MonoCommands("INTERNAL.mono_wasm_get_dbg_command_info()"); - public static MonoCommands GetDebuggerAgentBufferReceived() => new MonoCommands("MONO.mono_wasm_get_dbg_command_info()"); + public static MonoCommands IsRuntimeReady() => new MonoCommands("INTERNAL.mono_wasm_runtime_is_ready"); - 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 GetLoadedFiles() => new MonoCommands("INTERNAL.mono_wasm_get_loaded_files()"); public static MonoCommands SendDebuggerAgentCommand(int id, int command_set, int command, string command_parameters) { - return new MonoCommands($"MONO.mono_wasm_send_dbg_command ({id}, {command_set}, {command},'{command_parameters}')"); + return new MonoCommands($"INTERNAL.mono_wasm_send_dbg_command ({id}, {command_set}, {command},'{command_parameters}')"); } public static MonoCommands SendDebuggerAgentCommandWithParms(int id, int command_set, int command, string command_parameters, int len, int type, string parm) { - return new MonoCommands($"MONO.mono_wasm_send_dbg_command_with_parms ({id}, {command_set}, {command},'{command_parameters}', {len}, {type}, '{parm}')"); + return new MonoCommands($"INTERNAL.mono_wasm_send_dbg_command_with_parms ({id}, {command_set}, {command},'{command_parameters}', {len}, {type}, '{parm}')"); } - public static MonoCommands CallFunctionOn(JToken args) => new MonoCommands($"MONO.mono_wasm_call_function_on ({args.ToString()})"); + public static MonoCommands CallFunctionOn(JToken args) => new MonoCommands($"INTERNAL.mono_wasm_call_function_on ({args})"); - public static MonoCommands GetDetails(int objectId, JToken args = null) => new MonoCommands($"MONO.mono_wasm_get_details ({objectId}, {(args ?? "{ }")})"); + public static MonoCommands GetDetails(int objectId, JToken args = null) => new MonoCommands($"INTERNAL.mono_wasm_get_details ({objectId}, {(args ?? "{ }")})"); - public static MonoCommands Resume() => new MonoCommands($"MONO.mono_wasm_debugger_resume ()"); + public static MonoCommands Resume() => new MonoCommands($"INTERNAL.mono_wasm_debugger_resume ()"); - public static MonoCommands DetachDebugger() => new MonoCommands($"MONO.mono_wasm_detach_debugger()"); + public static MonoCommands DetachDebugger() => new MonoCommands($"INTERNAL.mono_wasm_detach_debugger()"); - public static MonoCommands ReleaseObject(DotnetObjectId objectId) => new MonoCommands($"MONO.mono_wasm_release_object('{objectId}')"); + public static MonoCommands ReleaseObject(DotnetObjectId objectId) => new MonoCommands($"INTERNAL.mono_wasm_release_object('{objectId}')"); } internal enum MonoErrorCodes diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs index 6e9d420801dd9..ecd9070a8e21d 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs @@ -35,7 +35,7 @@ public async Task CreateJSBreakpoint() { // Test that js breakpoints get set correctly // 13 24 - // 13 31 + // 13 33 var bp1_res = await SetBreakpoint("/debugger-driver.html", 13, 24); Assert.EndsWith("debugger-driver.html", bp1_res.Value["breakpointId"].ToString()); @@ -47,7 +47,7 @@ public async Task CreateJSBreakpoint() Assert.Equal(13, loc["lineNumber"]); Assert.Equal(24, loc["columnNumber"]); - var bp2_res = await SetBreakpoint("/debugger-driver.html", 13, 31); + var bp2_res = await SetBreakpoint("/debugger-driver.html", 13, 33); Assert.EndsWith("debugger-driver.html", bp2_res.Value["breakpointId"].ToString()); Assert.Equal(1, bp2_res.Value["locations"]?.Value()?.Count); @@ -56,14 +56,14 @@ public async Task CreateJSBreakpoint() Assert.NotNull(loc2["scriptId"]); Assert.Equal(13, loc2["lineNumber"]); - Assert.Equal(31, loc2["columnNumber"]); + Assert.Equal(33, loc2["columnNumber"]); } [Fact] public async Task CreateJS0Breakpoint() { // 13 24 - // 13 31 + // 13 33 var bp1_res = await SetBreakpoint("/debugger-driver.html", 13, 0); Assert.EndsWith("debugger-driver.html", bp1_res.Value["breakpointId"].ToString()); @@ -75,7 +75,7 @@ public async Task CreateJS0Breakpoint() Assert.Equal(13, loc["lineNumber"]); Assert.Equal(24, loc["columnNumber"]); - var bp2_res = await SetBreakpoint("/debugger-driver.html", 13, 31); + var bp2_res = await SetBreakpoint("/debugger-driver.html", 13, 33); Assert.EndsWith("debugger-driver.html", bp2_res.Value["breakpointId"].ToString()); Assert.Equal(1, bp2_res.Value["locations"]?.Value()?.Count); @@ -84,7 +84,7 @@ public async Task CreateJS0Breakpoint() Assert.NotNull(loc2["scriptId"]); Assert.Equal(13, loc2["lineNumber"]); - Assert.Equal(31, loc2["columnNumber"]); + Assert.Equal(33, loc2["columnNumber"]); } [Theory] diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs index 7e78bc0e2b64c..bd92af72637ad 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs @@ -41,7 +41,12 @@ protected static string DebuggerTestAppPath static protected string FindTestPath() { var asm_dir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); - var test_app_path = Path.Combine(asm_dir, "..", "..", "..", "debugger-test", "Debug", "publish"); +#if DEBUG + var config="Debug"; +#else + var config="Release"; +#endif + var test_app_path = Path.Combine(asm_dir, "..", "..", "..", "debugger-test", config, "publish"); if (File.Exists(Path.Combine(test_app_path, "debugger-driver.html"))) return test_app_path; diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/MonoJsTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/MonoJsTests.cs index b45825acfcbd8..38742d8d36330 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/MonoJsTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/MonoJsTests.cs @@ -13,41 +13,17 @@ namespace DebuggerTests { public class MonoJsTests : DebuggerTestBase { - [Fact] - public async Task InvalidScopeId() - { - var bp1_res = await SetBreakpointInMethod("debugger-test.dll", "Math", "IntAdd", 3); - await EvaluateAndCheck( - "window.setTimeout(function() { invoke_static_method('[debugger-test] Math:IntAdd', 1, 2); })", - null, -1, -1, "IntAdd"); - - var varIds = new[] - { - new { index = 0, name = "one" }, - }; - - var scopeId = "-12"; - var expression = $"MONO.mono_wasm_get_variables({scopeId}, {JsonConvert.SerializeObject(varIds)})"; - var res = await cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression, returnByValue = true }), token); - Assert.False(res.IsOk); - - scopeId = "30000"; - expression = $"MONO.mono_wasm_get_variables({scopeId}, {JsonConvert.SerializeObject(varIds)})"; - res = await cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression, returnByValue = true }), token); - Assert.False(res.IsOk); - } - [Fact] public async Task BadRaiseDebugEventsTest() { var bad_expressions = new[] { - "MONO.mono_wasm_raise_debug_event('')", - "MONO.mono_wasm_raise_debug_event(undefined)", - "MONO.mono_wasm_raise_debug_event({})", + "INTERNAL.mono_wasm_raise_debug_event('')", + "INTERNAL.mono_wasm_raise_debug_event(undefined)", + "INTERNAL.mono_wasm_raise_debug_event({})", - "MONO.mono_wasm_raise_debug_event({eventName:'foo'}, '')", - "MONO.mono_wasm_raise_debug_event({eventName:'foo'}, 12)" + "INTERNAL.mono_wasm_raise_debug_event({eventName:'foo'}, '')", + "INTERNAL.mono_wasm_raise_debug_event({eventName:'foo'}, 12)" }; foreach (var expression in bad_expressions) @@ -82,7 +58,7 @@ public async Task RaiseDebugEventTraceTest(bool? trace) }); var trace_str = trace.HasValue ? $"trace: {trace.ToString().ToLower()}" : String.Empty; - var expression = $"MONO.mono_wasm_raise_debug_event({{ eventName:'qwe' }}, {{ {trace_str} }})"; + var expression = $"INTERNAL.mono_wasm_raise_debug_event({{ eventName:'qwe' }}, {{ {trace_str} }})"; var res = await cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression }), token); Assert.True(res.IsOk, $"Expected to pass for {expression}"); @@ -163,7 +139,7 @@ async Task AssemblyLoadedEventTest(string asm_name, string asm_path, string pdb_ pdb_base64 = Convert.ToBase64String(bytes); } - var expression = $@"MONO.mono_wasm_raise_debug_event({{ + var expression = $@"INTERNAL.mono_wasm_raise_debug_event({{ eventName: 'AssemblyLoaded', assembly_name: '{asm_name}', assembly_b64: '{asm_base64}', 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 b1bfcd859d064..8f6902ca0f0de 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/debugger-driver.html +++ b/src/mono/wasm/debugger/tests/debugger-test/debugger-driver.html @@ -6,14 +6,14 @@