зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1730426 - Start explicitly throwing uncatchable exceptions. r=jandem
Add Interrupt status to JS::ExceptionStatus and set it when intentionally throwing an uncatchable exception. This is only a guideline for now, so avoid excessive asserts for now. Differential Revision: https://phabricator.services.mozilla.com/D125363
This commit is contained in:
Родитель
4d190c6349
Коммит
811579616a
|
@ -39,6 +39,12 @@ extern JS_PUBLIC_API void JS_SetPendingException(
|
||||||
JSContext* cx, JS::HandleValue v,
|
JSContext* cx, JS::HandleValue v,
|
||||||
JS::ExceptionStackBehavior behavior = JS::ExceptionStackBehavior::Capture);
|
JS::ExceptionStackBehavior behavior = JS::ExceptionStackBehavior::Capture);
|
||||||
|
|
||||||
|
// Indicate that we are intentionally raising an interrupt/uncatchable
|
||||||
|
// exception. Returning false/nullptr without calling this will still be treated
|
||||||
|
// as an interrupt, but in the future the implicit behaviour may no longer be
|
||||||
|
// allowed.
|
||||||
|
extern JS_PUBLIC_API void JS_SetPendingInterrupt(JSContext* cx);
|
||||||
|
|
||||||
extern JS_PUBLIC_API void JS_ClearPendingException(JSContext* cx);
|
extern JS_PUBLIC_API void JS_ClearPendingException(JSContext* cx);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -56,7 +62,9 @@ namespace JS {
|
||||||
// When propagating an exception up the call stack, we store the underlying
|
// When propagating an exception up the call stack, we store the underlying
|
||||||
// reason on the JSContext as one of the following enum values.
|
// reason on the JSContext as one of the following enum values.
|
||||||
//
|
//
|
||||||
// TODO: Track uncatchable exceptions explicitly.
|
// NOTE: It is not yet a hard requirement for uncatchable exceptions to set
|
||||||
|
// status to Interrupt so rely on the last return value. If Interrupt is
|
||||||
|
// not set then the status will remain as None.
|
||||||
enum class ExceptionStatus {
|
enum class ExceptionStatus {
|
||||||
// No expection status.
|
// No expection status.
|
||||||
None,
|
None,
|
||||||
|
@ -66,6 +74,12 @@ enum class ExceptionStatus {
|
||||||
// non-error completion.
|
// non-error completion.
|
||||||
ForcedReturn,
|
ForcedReturn,
|
||||||
|
|
||||||
|
// An uncatchable exception that functions as an interrupt. This is used for
|
||||||
|
// watchdog mechanisms (like the slow-script notification) or the debugger
|
||||||
|
// API. It cannot be caught be JS catch blocks, but the debugger or event
|
||||||
|
// loops may still capture it.
|
||||||
|
Interrupt,
|
||||||
|
|
||||||
// Throwing a (catchable) exception. Certain well-known exceptions are
|
// Throwing a (catchable) exception. Certain well-known exceptions are
|
||||||
// explicitly tracked for convenience.
|
// explicitly tracked for convenience.
|
||||||
Throwing,
|
Throwing,
|
||||||
|
|
|
@ -3680,7 +3680,7 @@ static bool Terminate(JSContext* cx, unsigned arg, Value* vp) {
|
||||||
fprintf(stderr, "terminate called\n");
|
fprintf(stderr, "terminate called\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_ClearPendingException(cx);
|
cx->setInterrupting();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -300,7 +300,7 @@ static void PropagateForcedReturn(JSContext* cx, AbstractFramePtr frame,
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
case ResumeMode::Terminate:
|
case ResumeMode::Terminate:
|
||||||
cx->clearPendingException();
|
cx->setInterrupting();
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
case ResumeMode::Return:
|
case ResumeMode::Return:
|
||||||
|
@ -1018,7 +1018,7 @@ NativeResumeMode DebugAPI::slowPathOnNativeCall(JSContext* cx,
|
||||||
return NativeResumeMode::Abort;
|
return NativeResumeMode::Abort;
|
||||||
|
|
||||||
case ResumeMode::Terminate:
|
case ResumeMode::Terminate:
|
||||||
cx->clearPendingException();
|
cx->setInterrupting();
|
||||||
return NativeResumeMode::Abort;
|
return NativeResumeMode::Abort;
|
||||||
|
|
||||||
case ResumeMode::Return:
|
case ResumeMode::Return:
|
||||||
|
@ -1951,6 +1951,7 @@ Completion Completion::fromJSResult(JSContext* cx, bool ok, const Value& rv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cx->isExceptionPending()) {
|
if (!cx->isExceptionPending()) {
|
||||||
|
cx->clearInterrupt();
|
||||||
return Completion(Terminate());
|
return Completion(Terminate());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -524,7 +524,7 @@ again:
|
||||||
}
|
}
|
||||||
|
|
||||||
// We may be propagating a forced return from a debugger hook function.
|
// We may be propagating a forced return from a debugger hook function.
|
||||||
if (MOZ_UNLIKELY(cx->isPropagatingForcedReturn())) {
|
if (cx->isPropagatingForcedReturn()) {
|
||||||
cx->clearPropagatingForcedReturn();
|
cx->clearPropagatingForcedReturn();
|
||||||
frameOk = true;
|
frameOk = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3685,6 +3685,10 @@ JS_PUBLIC_API void JS_SetPendingException(JSContext* cx, HandleValue value,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JS_PUBLIC_API void JS_SetPendingInterrupt(JSContext* cx) {
|
||||||
|
cx->setInterrupting();
|
||||||
|
}
|
||||||
|
|
||||||
JS_PUBLIC_API void JS_ClearPendingException(JSContext* cx) {
|
JS_PUBLIC_API void JS_ClearPendingException(JSContext* cx) {
|
||||||
AssertHeapIsIdle();
|
AssertHeapIsIdle();
|
||||||
cx->clearPendingException();
|
cx->clearPendingException();
|
||||||
|
|
|
@ -2617,6 +2617,7 @@ static bool Evaluate(JSContext* cx, unsigned argc, Value* vp) {
|
||||||
? JS_ExecuteScript(cx, script, args.rval())
|
? JS_ExecuteScript(cx, script, args.rval())
|
||||||
: JS_ExecuteScript(cx, envChain, script, args.rval()))) {
|
: JS_ExecuteScript(cx, envChain, script, args.rval()))) {
|
||||||
if (catchTermination && !JS_IsExceptionPending(cx)) {
|
if (catchTermination && !JS_IsExceptionPending(cx)) {
|
||||||
|
cx->clearInterrupt();
|
||||||
JSAutoRealm ar1(cx, callerGlobal);
|
JSAutoRealm ar1(cx, callerGlobal);
|
||||||
JSString* str = JS_NewStringCopyZ(cx, "terminated");
|
JSString* str = JS_NewStringCopyZ(cx, "terminated");
|
||||||
if (!str) {
|
if (!str) {
|
||||||
|
@ -3147,6 +3148,8 @@ static bool Quit(JSContext* cx, unsigned argc, Value* vp) {
|
||||||
js::StopDrainingJobQueue(cx);
|
js::StopDrainingJobQueue(cx);
|
||||||
sc->exitCode = code;
|
sc->exitCode = code;
|
||||||
sc->quitting = true;
|
sc->quitting = true;
|
||||||
|
|
||||||
|
cx->setInterrupting();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1220,7 +1220,7 @@ again:
|
||||||
UnwindIteratorsForUncatchableException(cx, regs);
|
UnwindIteratorsForUncatchableException(cx, regs);
|
||||||
|
|
||||||
// We may be propagating a forced return from a debugger hook function.
|
// We may be propagating a forced return from a debugger hook function.
|
||||||
if (MOZ_UNLIKELY(cx->isPropagatingForcedReturn())) {
|
if (cx->isPropagatingForcedReturn()) {
|
||||||
cx->clearPropagatingForcedReturn();
|
cx->clearPropagatingForcedReturn();
|
||||||
ok = true;
|
ok = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -837,6 +837,7 @@ void InternalJobQueue::runJobs(JSContext* cx) {
|
||||||
if (!JS::Call(cx, UndefinedHandleValue, job, args, &rval)) {
|
if (!JS::Call(cx, UndefinedHandleValue, job, args, &rval)) {
|
||||||
// Nothing we can do about uncatchable exceptions.
|
// Nothing we can do about uncatchable exceptions.
|
||||||
if (!cx->isExceptionPending()) {
|
if (!cx->isExceptionPending()) {
|
||||||
|
cx->clearInterrupt();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
RootedValue exn(cx);
|
RootedValue exn(cx);
|
||||||
|
|
|
@ -799,6 +799,18 @@ struct JS_PUBLIC_API JSContext : public JS::RootingContext,
|
||||||
return JS::IsCatchableExceptionStatus(status);
|
return JS::IsCatchableExceptionStatus(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setInterrupting() {
|
||||||
|
MOZ_ASSERT(status == JS::ExceptionStatus::None);
|
||||||
|
status = JS::ExceptionStatus::Interrupt;
|
||||||
|
}
|
||||||
|
void clearInterrupt() {
|
||||||
|
// NOTE: Calling `setInterrupting` is still optional, but certainly there
|
||||||
|
// should not be any catchable exception pending.
|
||||||
|
MOZ_ASSERT(status == JS::ExceptionStatus::None ||
|
||||||
|
status == JS::ExceptionStatus::Interrupt);
|
||||||
|
status = JS::ExceptionStatus::None;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool getPendingException(JS::MutableHandleValue rval);
|
[[nodiscard]] bool getPendingException(JS::MutableHandleValue rval);
|
||||||
|
|
||||||
js::SavedFrame* getPendingExceptionStack();
|
js::SavedFrame* getPendingExceptionStack();
|
||||||
|
|
|
@ -482,6 +482,8 @@ static bool HandleInterrupt(JSContext* cx, bool invokeCallback) {
|
||||||
chars = u"(stack not available)";
|
chars = u"(stack not available)";
|
||||||
}
|
}
|
||||||
WarnNumberUC(cx, JSMSG_TERMINATED, chars);
|
WarnNumberUC(cx, JSMSG_TERMINATED, chars);
|
||||||
|
|
||||||
|
cx->setInterrupting();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -423,6 +423,7 @@ static bool Quit(JSContext* cx, unsigned argc, Value* vp) {
|
||||||
|
|
||||||
gQuitting = true;
|
gQuitting = true;
|
||||||
// exit(0);
|
// exit(0);
|
||||||
|
JS_SetPendingInterrupt(cx);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче