diff options
-rw-r--r-- | CHANGELOG.md | 3 | ||||
-rw-r--r-- | src/support/file.cpp | 14 | ||||
-rw-r--r-- | src/support/utilities.h | 25 |
3 files changed, 30 insertions, 12 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index eafb64471..2298928bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,9 @@ Current Trunk - `TypeBuilderSetSubType` now takes a supertype as the second argument. - `call_ref` can now take a signature type immediate in the text format. The type immediate will become mandatory in the future. +- If `THROW_ON_FATAL` is defined at compile-time, then fatal errors will throw a + `std::runtime_error` instead of terminating the process. This may be used by + embedders of Binaryen to recover from errors. v110 ---- diff --git a/src/support/file.cpp b/src/support/file.cpp index dc9707a1b..cfd656391 100644 --- a/src/support/file.cpp +++ b/src/support/file.cpp @@ -16,6 +16,7 @@ #include "support/file.h" #include "support/debug.h" +#include "support/utilities.h" #include <cstdint> #include <cstdlib> @@ -58,18 +59,16 @@ T wasm::read_file(const std::string& filename, Flags::BinaryOption binary) { } infile.open(filename, flags); if (!infile.is_open()) { - std::cerr << "Failed opening '" << filename << "'" << std::endl; - exit(EXIT_FAILURE); + Fatal() << "Failed opening '" << filename << "'"; } infile.seekg(0, std::ios::end); std::streampos insize = infile.tellg(); if (uint64_t(insize) >= std::numeric_limits<size_t>::max()) { // Building a 32-bit executable where size_t == 32 bits, we are not able to // create strings larger than 2^32 bytes in length, so must abort here. - std::cerr << "Failed opening '" << filename - << "': Input file too large: " << insize - << " bytes. Try rebuilding in 64-bit mode." << std::endl; - exit(EXIT_FAILURE); + Fatal() << "Failed opening '" << filename + << "': Input file too large: " << insize + << " bytes. Try rebuilding in 64-bit mode."; } T input(size_t(insize) + (binary == Flags::Binary ? 0 : 1), '\0'); if (size_t(insize) == 0) { @@ -115,8 +114,7 @@ wasm::Output::Output(const std::string& filename, Flags::BinaryOption binary) } outfile.open(filename, flags); if (!outfile.is_open()) { - std::cerr << "Failed opening '" << filename << "'" << std::endl; - exit(EXIT_FAILURE); + Fatal() << "Failed opening '" << filename << "'"; } buffer = outfile.rdbuf(); } diff --git a/src/support/utilities.h b/src/support/utilities.h index a8acfeed7..abff70453 100644 --- a/src/support/utilities.h +++ b/src/support/utilities.h @@ -24,6 +24,7 @@ #include <cstring> #include <iostream> #include <memory> +#include <sstream> #include <type_traits> #include "support/bits.h" @@ -62,19 +63,35 @@ std::unique_ptr<T> make_unique(Args&&... args) { // For fatal errors which could arise from input (i.e. not assertion failures) class Fatal { +private: + std::stringstream buffer; + public: - Fatal() { std::cerr << "Fatal: "; } + Fatal() { buffer << "Fatal: "; } template<typename T> Fatal& operator<<(T&& arg) { - std::cerr << arg; + buffer << arg; return *this; } +#ifndef THROW_ON_FATAL [[noreturn]] ~Fatal() { - std::cerr << "\n"; + std::cerr << buffer.str() << std::endl; // Use _Exit here to avoid calling static destructors. This avoids deadlocks // in (for example) the thread worker pool, where workers hold a lock while // performing their work. - _Exit(1); + _Exit(EXIT_FAILURE); + } +#else + // This variation is a best-effort attempt to make fatal errors recoverable + // for embedders of Binaryen as a library, namely wasm-opt-rs. + // + // Throwing in destructors is strongly discouraged, since it is easy to + // accidentally throw during unwinding, which will trigger an abort. Since + // `Fatal` is a special type that only occurs on error paths, we are hoping it + // is never constructed during unwinding or while destructing another type. + [[noreturn]] ~Fatal() noexcept(false) { + throw std::runtime_error(buffer.str()); } +#endif }; [[noreturn]] void handle_unreachable(const char* msg = nullptr, |