summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBrian Anderson <andersrb@gmail.com>2022-09-30 19:25:06 -0500
committerGitHub <noreply@github.com>2022-10-01 00:25:06 +0000
commit22f0f3ae45c9dc7d091282974d013ab340754b6e (patch)
treef3fad3969e95e82779137e7dc1cd6b5f019077bc /src
parenta697046975cb2f952d7cc1e67474f7cd000ed027 (diff)
downloadbinaryen-22f0f3ae45c9dc7d091282974d013ab340754b6e.tar.gz
binaryen-22f0f3ae45c9dc7d091282974d013ab340754b6e.tar.bz2
binaryen-22f0f3ae45c9dc7d091282974d013ab340754b6e.zip
Change `exit()` to `Fatal()`; make it possible to throw on `Fatal()`. (#5087)
This patch makes binaryen easier to call from other applications by making more errors recoverable instead of early-exiting. The main thing it does is change three calls to exit on I/O errors into calls to Fatal(), which is an existing custom abstraction for handling unrecoverable errors. Currently Fatal's destructor calls _Exit(1). My intent is to make it possible for Fatal to not exit, but to throw, allowing an embedding application to catch the exception. Because the previous early exits were exiting with error code EXIT_FAILURE, I also changed Fatal to exit with EXIT_FAILURE. The test suite continues to pass so I assume this is ok. Next I changed Fatal to buffer its error message until the destructor instead of immediately printing it to stderr. This is for ease of patching Fatal to throw instead. Finally, I also included the patch I need to make Fatal throw when THROW_ON_FATAL is defined at compile time. I can carry this patch out of tree, but it is a small patch, so perhaps you will be willing to take it. I am happy to remove it. Fixes #4938
Diffstat (limited to 'src')
-rw-r--r--src/support/file.cpp14
-rw-r--r--src/support/utilities.h25
2 files changed, 27 insertions, 12 deletions
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,