Only intercept JSIExceptions when constructing JSError
Summary: Constructing a `JSError` currently catches all exceptions that occur, regardless of their type, and embeds the information from them in the newly created `JSError`. This means that the type of exceptions can be silently changed. This results in a bug if Hermes is built with `HERMESVM_EXCEPTION_ON_OOM` enabled. The OOM exception will get converted to a `JSError`, which in turn can get converted into a regular JS exception. The regular JS exception can then be caught by JS and ignored. This leads to the VM continuing to execute in a bad state. I considered three ways to fix this: 1. Add a new type of exception to JSI that is intentionally meant to be ignored, and the Hermes OOM exception then subclasses it. 2. Propagate all exceptions that happen when constructing `JSError`. 3. Propagate all exceptions except `JSIException`s when constructing `JSError`. The first is technically the most surgical, but it adds complexity to JSI, and will require some extra machinery to implement since we wouldn't be able to throw it directly from inside the VM. I'm also not comfortable with the idea of JSI suppressing exceptions that are completely unrelated to JSI. The second is the simplest, and seems to match what JS would do if `throw new Error()` itself threw, but there is some possibility that it is a change in behaviour for existing code. So the third approach, which is implemented in this diff, tries to compromise between the two. Hermes can only ever throw `JSIException`s from regular JS operations, so there should be no change for existing code (except code that uses exceptions on OOM, which is what we're trying to fix). Any other exception gets passed through to the caller. Changelog: [Internal][Fixed] - Fixed handling of Hermes OOM exceptions in JSI. Reviewed By: jpporto Differential Revision: D41831616 fbshipit-source-id: 42e0dde1c4acc016ab19533941c58fc1797ba1c2
This commit is contained in:
Родитель
8fbcb5cf94
Коммит
cd5a8c48c5
|
@ -427,11 +427,9 @@ JSError::JSError(Runtime& rt, std::string msg) : message_(std::move(msg)) {
|
|||
setValue(
|
||||
rt,
|
||||
callGlobalFunction(rt, "Error", String::createFromUtf8(rt, message_)));
|
||||
} catch (const std::exception& ex) {
|
||||
} catch (const JSIException& ex) {
|
||||
message_ = std::string(ex.what()) + " (while raising " + message_ + ")";
|
||||
setValue(rt, String::createFromUtf8(rt, message_));
|
||||
} catch (...) {
|
||||
setValue(rt, Value());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -442,10 +440,8 @@ JSError::JSError(Runtime& rt, std::string msg, std::string stack)
|
|||
e.setProperty(rt, "message", String::createFromUtf8(rt, message_));
|
||||
e.setProperty(rt, "stack", String::createFromUtf8(rt, stack_));
|
||||
setValue(rt, std::move(e));
|
||||
} catch (const std::exception& ex) {
|
||||
} catch (const JSIException& ex) {
|
||||
setValue(rt, String::createFromUtf8(rt, ex.what()));
|
||||
} catch (...) {
|
||||
setValue(rt, Value());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -457,74 +453,68 @@ JSError::JSError(std::string what, Runtime& rt, Value&& value)
|
|||
void JSError::setValue(Runtime& rt, Value&& value) {
|
||||
value_ = std::make_shared<Value>(std::move(value));
|
||||
|
||||
try {
|
||||
if ((message_.empty() || stack_.empty()) && value_->isObject()) {
|
||||
auto obj = value_->getObject(rt);
|
||||
|
||||
if (message_.empty()) {
|
||||
try {
|
||||
Value message = obj.getProperty(rt, "message");
|
||||
if (!message.isUndefined() && !message.isString()) {
|
||||
message = callGlobalFunction(rt, "String", message);
|
||||
}
|
||||
if (message.isString()) {
|
||||
message_ = message.getString(rt).utf8(rt);
|
||||
} else if (!message.isUndefined()) {
|
||||
message_ = "String(e.message) is a " + kindToString(message, &rt);
|
||||
}
|
||||
} catch (const std::exception& ex) {
|
||||
message_ = std::string("[Exception while creating message string: ") +
|
||||
ex.what() + "]";
|
||||
}
|
||||
}
|
||||
|
||||
if (stack_.empty()) {
|
||||
try {
|
||||
Value stack = obj.getProperty(rt, "stack");
|
||||
if (!stack.isUndefined() && !stack.isString()) {
|
||||
stack = callGlobalFunction(rt, "String", stack);
|
||||
}
|
||||
if (stack.isString()) {
|
||||
stack_ = stack.getString(rt).utf8(rt);
|
||||
} else if (!stack.isUndefined()) {
|
||||
stack_ = "String(e.stack) is a " + kindToString(stack, &rt);
|
||||
}
|
||||
} catch (const std::exception& ex) {
|
||||
message_ = std::string("[Exception while creating stack string: ") +
|
||||
ex.what() + "]";
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((message_.empty() || stack_.empty()) && value_->isObject()) {
|
||||
auto obj = value_->getObject(rt);
|
||||
|
||||
if (message_.empty()) {
|
||||
try {
|
||||
if (value_->isString()) {
|
||||
message_ = value_->getString(rt).utf8(rt);
|
||||
} else {
|
||||
Value message = callGlobalFunction(rt, "String", *value_);
|
||||
if (message.isString()) {
|
||||
message_ = message.getString(rt).utf8(rt);
|
||||
} else {
|
||||
message_ = "String(e) is a " + kindToString(message, &rt);
|
||||
}
|
||||
Value message = obj.getProperty(rt, "message");
|
||||
if (!message.isUndefined() && !message.isString()) {
|
||||
message = callGlobalFunction(rt, "String", message);
|
||||
}
|
||||
} catch (const std::exception& ex) {
|
||||
if (message.isString()) {
|
||||
message_ = message.getString(rt).utf8(rt);
|
||||
} else if (!message.isUndefined()) {
|
||||
message_ = "String(e.message) is a " + kindToString(message, &rt);
|
||||
}
|
||||
} catch (const JSIException& ex) {
|
||||
message_ = std::string("[Exception while creating message string: ") +
|
||||
ex.what() + "]";
|
||||
}
|
||||
}
|
||||
|
||||
if (stack_.empty()) {
|
||||
stack_ = "no stack";
|
||||
try {
|
||||
Value stack = obj.getProperty(rt, "stack");
|
||||
if (!stack.isUndefined() && !stack.isString()) {
|
||||
stack = callGlobalFunction(rt, "String", stack);
|
||||
}
|
||||
if (stack.isString()) {
|
||||
stack_ = stack.getString(rt).utf8(rt);
|
||||
} else if (!stack.isUndefined()) {
|
||||
stack_ = "String(e.stack) is a " + kindToString(stack, &rt);
|
||||
}
|
||||
} catch (const JSIException& ex) {
|
||||
message_ = std::string("[Exception while creating stack string: ") +
|
||||
ex.what() + "]";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (what_.empty()) {
|
||||
what_ = message_ + "\n\n" + stack_;
|
||||
if (message_.empty()) {
|
||||
try {
|
||||
if (value_->isString()) {
|
||||
message_ = value_->getString(rt).utf8(rt);
|
||||
} else {
|
||||
Value message = callGlobalFunction(rt, "String", *value_);
|
||||
if (message.isString()) {
|
||||
message_ = message.getString(rt).utf8(rt);
|
||||
} else {
|
||||
message_ = "String(e) is a " + kindToString(message, &rt);
|
||||
}
|
||||
}
|
||||
} catch (const JSIException& ex) {
|
||||
message_ = std::string("[Exception while creating message string: ") +
|
||||
ex.what() + "]";
|
||||
}
|
||||
} catch (...) {
|
||||
message_ = "[Exception caught creating message string]";
|
||||
stack_ = "[Exception caught creating stack string]";
|
||||
what_ = "[Exception caught getting value fields]";
|
||||
}
|
||||
|
||||
if (stack_.empty()) {
|
||||
stack_ = "no stack";
|
||||
}
|
||||
|
||||
if (what_.empty()) {
|
||||
what_ = message_ + "\n\n" + stack_;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче