summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md3
-rw-r--r--src/support/file.cpp14
-rw-r--r--src/support/utilities.h25
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,