mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-23 18:26:15 +00:00
Bug 1212349 - Encapsulate the shell's per-runtime state r=jandem
This commit is contained in:
parent
db67be8145
commit
6c9c84a8e4
@ -109,30 +109,53 @@ enum JSShellExitCode {
|
||||
EXITCODE_TIMEOUT = 6
|
||||
};
|
||||
|
||||
static size_t gStackChunkSize = 8192;
|
||||
static const size_t gStackChunkSize = 8192;
|
||||
|
||||
/*
|
||||
* Note: This limit should match the stack limit set by the browser in
|
||||
* js/xpconnect/src/XPCJSRuntime.cpp
|
||||
*/
|
||||
#if defined(MOZ_ASAN) || (defined(DEBUG) && !defined(XP_WIN))
|
||||
static size_t gMaxStackSize = 2 * 128 * sizeof(size_t) * 1024;
|
||||
static const size_t gMaxStackSize = 2 * 128 * sizeof(size_t) * 1024;
|
||||
#else
|
||||
static size_t gMaxStackSize = 128 * sizeof(size_t) * 1024;
|
||||
static const size_t gMaxStackSize = 128 * sizeof(size_t) * 1024;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Limit the timeout to 30 minutes to prevent an overflow on platfoms
|
||||
* that represent the time internally in microseconds using 32-bit int.
|
||||
*/
|
||||
static double MAX_TIMEOUT_INTERVAL = 1800.0;
|
||||
static double gTimeoutInterval = -1.0;
|
||||
static Atomic<bool> gServiceInterrupt;
|
||||
static JS::PersistentRootedValue gInterruptFunc;
|
||||
static const double MAX_TIMEOUT_INTERVAL = 1800.0;
|
||||
|
||||
static bool gLastWarningEnabled = false;
|
||||
static JS::PersistentRootedValue gLastWarning;
|
||||
// Per-runtime shell state.
|
||||
struct ShellRuntime
|
||||
{
|
||||
ShellRuntime();
|
||||
|
||||
bool isWorker;
|
||||
double timeoutInterval;
|
||||
Atomic<bool> serviceInterrupt;
|
||||
JS::PersistentRootedValue interruptFunc;
|
||||
bool lastWarningEnabled;
|
||||
JS::PersistentRootedValue lastWarning;
|
||||
|
||||
/*
|
||||
* Watchdog thread state.
|
||||
*/
|
||||
PRLock* watchdogLock;
|
||||
PRCondVar* watchdogWakeup;
|
||||
PRThread* watchdogThread;
|
||||
bool watchdogHasTimeout;
|
||||
int64_t watchdogTimeout;
|
||||
|
||||
PRCondVar* sleepWakeup;
|
||||
|
||||
int exitCode;
|
||||
bool quitting;
|
||||
bool gotError;
|
||||
};
|
||||
|
||||
// Shell state set once at startup.
|
||||
static bool enableCodeCoverage = false;
|
||||
static bool enableDisassemblyDumps = false;
|
||||
static bool offthreadCompilation = false;
|
||||
@ -144,11 +167,22 @@ static bool enableUnboxedArrays = false;
|
||||
#ifdef JS_GC_ZEAL
|
||||
static char gZealStr[128];
|
||||
#endif
|
||||
|
||||
static bool printTiming = false;
|
||||
static const char* jsCacheDir = nullptr;
|
||||
static const char* jsCacheAsmJSPath = nullptr;
|
||||
static bool jsCachingEnabled = false;
|
||||
static FILE* gErrFile = nullptr;
|
||||
static FILE* gOutFile = nullptr;
|
||||
static bool reportWarnings = true;
|
||||
static bool compileOnly = false;
|
||||
static bool fuzzingSafe = false;
|
||||
static bool disableOOMFunctions = false;
|
||||
#ifdef DEBUG
|
||||
static bool dumpEntrainedVariables = false;
|
||||
static bool OOM_printAllocationCount = false;
|
||||
#endif
|
||||
|
||||
// Shell state this is only accessed on the main thread.
|
||||
bool jsCachingEnabled = false;
|
||||
mozilla::Atomic<bool> jsCacheOpened(false);
|
||||
|
||||
static bool
|
||||
@ -158,7 +192,7 @@ static bool
|
||||
InitWatchdog(JSRuntime* rt);
|
||||
|
||||
static void
|
||||
KillWatchdog();
|
||||
KillWatchdog(JSRuntime *rt);
|
||||
|
||||
static bool
|
||||
ScheduleWatchdog(JSRuntime* rt, double t);
|
||||
@ -166,33 +200,6 @@ ScheduleWatchdog(JSRuntime* rt, double t);
|
||||
static void
|
||||
CancelExecution(JSRuntime* rt);
|
||||
|
||||
/*
|
||||
* Watchdog thread state.
|
||||
*/
|
||||
static PRLock* gWatchdogLock = nullptr;
|
||||
static PRCondVar* gWatchdogWakeup = nullptr;
|
||||
static PRThread* gWatchdogThread = nullptr;
|
||||
static bool gWatchdogHasTimeout = false;
|
||||
static int64_t gWatchdogTimeout = 0;
|
||||
|
||||
static PRCondVar* gSleepWakeup = nullptr;
|
||||
|
||||
static int gExitCode = 0;
|
||||
static bool gQuitting = false;
|
||||
static bool gGotError = false;
|
||||
static FILE* gErrFile = nullptr;
|
||||
static FILE* gOutFile = nullptr;
|
||||
|
||||
static bool reportWarnings = true;
|
||||
static bool compileOnly = false;
|
||||
static bool fuzzingSafe = false;
|
||||
static bool disableOOMFunctions = false;
|
||||
|
||||
#ifdef DEBUG
|
||||
static bool dumpEntrainedVariables = false;
|
||||
static bool OOM_printAllocationCount = false;
|
||||
#endif
|
||||
|
||||
static JSContext*
|
||||
NewContext(JSRuntime* rt);
|
||||
|
||||
@ -267,6 +274,36 @@ extern JS_EXPORT_API(void) add_history(char* line);
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
ShellRuntime::ShellRuntime()
|
||||
: isWorker(false),
|
||||
timeoutInterval(-1.0),
|
||||
serviceInterrupt(false),
|
||||
lastWarningEnabled(false),
|
||||
watchdogLock(nullptr),
|
||||
watchdogWakeup(nullptr),
|
||||
watchdogThread(nullptr),
|
||||
watchdogHasTimeout(false),
|
||||
watchdogTimeout(0),
|
||||
sleepWakeup(nullptr),
|
||||
exitCode(0),
|
||||
quitting(false),
|
||||
gotError(false)
|
||||
{}
|
||||
|
||||
static ShellRuntime*
|
||||
GetShellRuntime(JSRuntime *rt)
|
||||
{
|
||||
ShellRuntime* sr = static_cast<ShellRuntime*>(JS_GetRuntimePrivate(rt));
|
||||
MOZ_ASSERT(sr);
|
||||
return sr;
|
||||
}
|
||||
|
||||
static ShellRuntime*
|
||||
GetShellRuntime(JSContext* cx)
|
||||
{
|
||||
return GetShellRuntime(cx->runtime());
|
||||
}
|
||||
|
||||
static char*
|
||||
GetLine(FILE* file, const char * prompt)
|
||||
{
|
||||
@ -370,17 +407,18 @@ GetContextData(JSContext* cx)
|
||||
static bool
|
||||
ShellInterruptCallback(JSContext* cx)
|
||||
{
|
||||
if (!gServiceInterrupt)
|
||||
ShellRuntime* sr = GetShellRuntime(cx);
|
||||
if (!sr->serviceInterrupt)
|
||||
return true;
|
||||
|
||||
// Reset gServiceInterrupt. CancelExecution or InterruptIf will set it to
|
||||
// Reset serviceInterrupt. CancelExecution or InterruptIf will set it to
|
||||
// true to distinguish watchdog or user triggered interrupts.
|
||||
// Do this first to prevent other interrupts that may occur while the
|
||||
// user-supplied callback is executing from re-entering the handler.
|
||||
gServiceInterrupt = false;
|
||||
sr->serviceInterrupt = false;
|
||||
|
||||
bool result;
|
||||
RootedValue interruptFunc(cx, gInterruptFunc);
|
||||
RootedValue interruptFunc(cx, sr->interruptFunc);
|
||||
if (!interruptFunc.isNull()) {
|
||||
JS::AutoSaveExceptionState savedExc(cx);
|
||||
JSAutoCompartment ac(cx, &interruptFunc.toObject());
|
||||
@ -398,8 +436,8 @@ ShellInterruptCallback(JSContext* cx)
|
||||
result = false;
|
||||
}
|
||||
|
||||
if (!result && gExitCode == 0)
|
||||
gExitCode = EXITCODE_TIMEOUT;
|
||||
if (!result && sr->exitCode == 0)
|
||||
sr->exitCode = EXITCODE_TIMEOUT;
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -432,6 +470,8 @@ SkipUTF8BOM(FILE* file)
|
||||
static void
|
||||
RunFile(JSContext* cx, const char* filename, FILE* file, bool compileOnly)
|
||||
{
|
||||
ShellRuntime* sr = GetShellRuntime(cx);
|
||||
|
||||
SkipUTF8BOM(file);
|
||||
|
||||
// To support the UNIX #! shell hack, gobble the first line if it starts
|
||||
@ -456,9 +496,9 @@ RunFile(JSContext* cx, const char* filename, FILE* file, bool compileOnly)
|
||||
.setIsRunOnce(true)
|
||||
.setNoScriptRval(true);
|
||||
|
||||
gGotError = false;
|
||||
sr->gotError = false;
|
||||
(void) JS::Compile(cx, options, file, &script);
|
||||
MOZ_ASSERT_IF(!script, gGotError);
|
||||
MOZ_ASSERT_IF(!script, sr->gotError);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
@ -467,8 +507,8 @@ RunFile(JSContext* cx, const char* filename, FILE* file, bool compileOnly)
|
||||
#endif
|
||||
if (script && !compileOnly) {
|
||||
if (!JS_ExecuteScript(cx, script)) {
|
||||
if (!gQuitting && gExitCode != EXITCODE_TIMEOUT)
|
||||
gExitCode = EXITCODE_RUNTIME_ERROR;
|
||||
if (!sr->quitting && sr->exitCode != EXITCODE_TIMEOUT)
|
||||
sr->exitCode = EXITCODE_RUNTIME_ERROR;
|
||||
}
|
||||
int64_t t2 = PRMJ_Now() - t1;
|
||||
if (printTiming)
|
||||
@ -514,6 +554,7 @@ EvalAndPrint(JSContext* cx, const char* bytes, size_t length,
|
||||
static void
|
||||
ReadEvalPrintLoop(JSContext* cx, FILE* in, FILE* out, bool compileOnly)
|
||||
{
|
||||
ShellRuntime* sr = GetShellRuntime(cx);
|
||||
int lineno = 1;
|
||||
bool hitEOF = false;
|
||||
|
||||
@ -529,7 +570,7 @@ ReadEvalPrintLoop(JSContext* cx, FILE* in, FILE* out, bool compileOnly)
|
||||
CharBuffer buffer(cx);
|
||||
do {
|
||||
ScheduleWatchdog(cx->runtime(), -1);
|
||||
gServiceInterrupt = false;
|
||||
sr->serviceInterrupt = false;
|
||||
errno = 0;
|
||||
|
||||
char* line = GetLine(in, startline == lineno ? "js> " : "");
|
||||
@ -546,7 +587,7 @@ ReadEvalPrintLoop(JSContext* cx, FILE* in, FILE* out, bool compileOnly)
|
||||
return;
|
||||
|
||||
lineno++;
|
||||
if (!ScheduleWatchdog(cx->runtime(), gTimeoutInterval)) {
|
||||
if (!ScheduleWatchdog(cx->runtime(), sr->timeoutInterval)) {
|
||||
hitEOF = true;
|
||||
break;
|
||||
}
|
||||
@ -561,7 +602,7 @@ ReadEvalPrintLoop(JSContext* cx, FILE* in, FILE* out, bool compileOnly)
|
||||
// Catch the error, report it, and keep going.
|
||||
JS_ReportPendingException(cx);
|
||||
}
|
||||
} while (!hitEOF && !gQuitting);
|
||||
} while (!hitEOF && !sr->quitting);
|
||||
|
||||
fprintf(out, "\n");
|
||||
}
|
||||
@ -1543,6 +1584,8 @@ Help(JSContext* cx, unsigned argc, Value* vp);
|
||||
static bool
|
||||
Quit(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
ShellRuntime* sr = GetShellRuntime(cx);
|
||||
|
||||
#ifdef JS_MORE_DETERMINISTIC
|
||||
// Print a message to stderr in more-deterministic builds to help jsfunfuzz
|
||||
// find uncatchable-exception bugs.
|
||||
@ -1564,8 +1607,8 @@ Quit(JSContext* cx, unsigned argc, Value* vp)
|
||||
return false;
|
||||
}
|
||||
|
||||
gExitCode = code;
|
||||
gQuitting = true;
|
||||
sr->exitCode = code;
|
||||
sr->quitting = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -2545,9 +2588,25 @@ WorkerMain(void* arg)
|
||||
js_delete(input);
|
||||
return;
|
||||
}
|
||||
|
||||
mozilla::UniquePtr<ShellRuntime> sr = MakeUnique<ShellRuntime>();
|
||||
if (!sr) {
|
||||
JS_DestroyRuntime(rt);
|
||||
js_delete(input);
|
||||
return;
|
||||
}
|
||||
|
||||
sr->isWorker = true;
|
||||
JS_SetRuntimePrivate(rt, sr.get());
|
||||
JS_SetErrorReporter(rt, my_ErrorReporter);
|
||||
SetWorkerRuntimeOptions(rt);
|
||||
|
||||
if (!InitWatchdog(rt)) {
|
||||
JS_DestroyRuntime(rt);
|
||||
js_delete(input);
|
||||
return;
|
||||
}
|
||||
|
||||
JSContext* cx = NewContext(rt);
|
||||
if (!cx) {
|
||||
JS_DestroyRuntime(rt);
|
||||
@ -2653,6 +2712,7 @@ IsBefore(int64_t t1, int64_t t2)
|
||||
static bool
|
||||
Sleep_fn(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
ShellRuntime* sr = GetShellRuntime(cx);
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
int64_t t_ticks;
|
||||
|
||||
@ -2673,61 +2733,63 @@ Sleep_fn(JSContext* cx, unsigned argc, Value* vp)
|
||||
? 0
|
||||
: int64_t(PRMJ_USEC_PER_SEC * t_secs);
|
||||
}
|
||||
PR_Lock(gWatchdogLock);
|
||||
PR_Lock(sr->watchdogLock);
|
||||
int64_t to_wakeup = PRMJ_Now() + t_ticks;
|
||||
for (;;) {
|
||||
PR_WaitCondVar(gSleepWakeup, PR_MillisecondsToInterval(t_ticks / 1000));
|
||||
if (gServiceInterrupt)
|
||||
PR_WaitCondVar(sr->sleepWakeup, PR_MillisecondsToInterval(t_ticks / 1000));
|
||||
if (sr->serviceInterrupt)
|
||||
break;
|
||||
int64_t now = PRMJ_Now();
|
||||
if (!IsBefore(now, to_wakeup))
|
||||
break;
|
||||
t_ticks = to_wakeup - now;
|
||||
}
|
||||
PR_Unlock(gWatchdogLock);
|
||||
PR_Unlock(sr->watchdogLock);
|
||||
args.rval().setUndefined();
|
||||
return !gServiceInterrupt;
|
||||
return !sr->serviceInterrupt;
|
||||
}
|
||||
|
||||
static bool
|
||||
InitWatchdog(JSRuntime* rt)
|
||||
{
|
||||
MOZ_ASSERT(!gWatchdogThread);
|
||||
gWatchdogLock = PR_NewLock();
|
||||
if (gWatchdogLock) {
|
||||
gWatchdogWakeup = PR_NewCondVar(gWatchdogLock);
|
||||
if (gWatchdogWakeup) {
|
||||
gSleepWakeup = PR_NewCondVar(gWatchdogLock);
|
||||
if (gSleepWakeup)
|
||||
ShellRuntime* sr = GetShellRuntime(rt);
|
||||
MOZ_ASSERT(!sr->watchdogThread);
|
||||
sr->watchdogLock = PR_NewLock();
|
||||
if (sr->watchdogLock) {
|
||||
sr->watchdogWakeup = PR_NewCondVar(sr->watchdogLock);
|
||||
if (sr->watchdogWakeup) {
|
||||
sr->sleepWakeup = PR_NewCondVar(sr->watchdogLock);
|
||||
if (sr->sleepWakeup)
|
||||
return true;
|
||||
PR_DestroyCondVar(gWatchdogWakeup);
|
||||
PR_DestroyCondVar(sr->watchdogWakeup);
|
||||
}
|
||||
PR_DestroyLock(gWatchdogLock);
|
||||
PR_DestroyLock(sr->watchdogLock);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
KillWatchdog()
|
||||
KillWatchdog(JSRuntime* rt)
|
||||
{
|
||||
ShellRuntime* sr = GetShellRuntime(rt);
|
||||
PRThread* thread;
|
||||
|
||||
PR_Lock(gWatchdogLock);
|
||||
thread = gWatchdogThread;
|
||||
PR_Lock(sr->watchdogLock);
|
||||
thread = sr->watchdogThread;
|
||||
if (thread) {
|
||||
/*
|
||||
* The watchdog thread is running, tell it to terminate waking it up
|
||||
* if necessary.
|
||||
*/
|
||||
gWatchdogThread = nullptr;
|
||||
PR_NotifyCondVar(gWatchdogWakeup);
|
||||
sr->watchdogThread = nullptr;
|
||||
PR_NotifyCondVar(sr->watchdogWakeup);
|
||||
}
|
||||
PR_Unlock(gWatchdogLock);
|
||||
PR_Unlock(sr->watchdogLock);
|
||||
if (thread)
|
||||
PR_JoinThread(thread);
|
||||
PR_DestroyCondVar(gSleepWakeup);
|
||||
PR_DestroyCondVar(gWatchdogWakeup);
|
||||
PR_DestroyLock(gWatchdogLock);
|
||||
PR_DestroyCondVar(sr->sleepWakeup);
|
||||
PR_DestroyCondVar(sr->watchdogWakeup);
|
||||
PR_DestroyLock(sr->watchdogLock);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -2736,24 +2798,25 @@ WatchdogMain(void* arg)
|
||||
PR_SetCurrentThreadName("JS Watchdog");
|
||||
|
||||
JSRuntime* rt = (JSRuntime*) arg;
|
||||
ShellRuntime* sr = GetShellRuntime(rt);
|
||||
|
||||
PR_Lock(gWatchdogLock);
|
||||
while (gWatchdogThread) {
|
||||
PR_Lock(sr->watchdogLock);
|
||||
while (sr->watchdogThread) {
|
||||
int64_t now = PRMJ_Now();
|
||||
if (gWatchdogHasTimeout && !IsBefore(now, gWatchdogTimeout)) {
|
||||
if (sr->watchdogHasTimeout && !IsBefore(now, sr->watchdogTimeout)) {
|
||||
/*
|
||||
* The timeout has just expired. Request an interrupt callback
|
||||
* outside the lock.
|
||||
*/
|
||||
gWatchdogHasTimeout = false;
|
||||
PR_Unlock(gWatchdogLock);
|
||||
sr->watchdogHasTimeout = false;
|
||||
PR_Unlock(sr->watchdogLock);
|
||||
CancelExecution(rt);
|
||||
PR_Lock(gWatchdogLock);
|
||||
PR_Lock(sr->watchdogLock);
|
||||
|
||||
/* Wake up any threads doing sleep. */
|
||||
PR_NotifyAllCondVar(gSleepWakeup);
|
||||
PR_NotifyAllCondVar(sr->sleepWakeup);
|
||||
} else {
|
||||
if (gWatchdogHasTimeout) {
|
||||
if (sr->watchdogHasTimeout) {
|
||||
/*
|
||||
* Time hasn't expired yet. Simulate an interrupt callback
|
||||
* which doesn't abort execution.
|
||||
@ -2762,58 +2825,61 @@ WatchdogMain(void* arg)
|
||||
}
|
||||
|
||||
uint64_t sleepDuration = PR_INTERVAL_NO_TIMEOUT;
|
||||
if (gWatchdogHasTimeout)
|
||||
if (sr->watchdogHasTimeout)
|
||||
sleepDuration = PR_TicksPerSecond() / 10;
|
||||
mozilla::DebugOnly<PRStatus> status =
|
||||
PR_WaitCondVar(gWatchdogWakeup, sleepDuration);
|
||||
PR_WaitCondVar(sr->watchdogWakeup, sleepDuration);
|
||||
MOZ_ASSERT(status == PR_SUCCESS);
|
||||
}
|
||||
}
|
||||
PR_Unlock(gWatchdogLock);
|
||||
PR_Unlock(sr->watchdogLock);
|
||||
}
|
||||
|
||||
static bool
|
||||
ScheduleWatchdog(JSRuntime* rt, double t)
|
||||
{
|
||||
ShellRuntime* sr = GetShellRuntime(rt);
|
||||
|
||||
if (t <= 0) {
|
||||
PR_Lock(gWatchdogLock);
|
||||
gWatchdogHasTimeout = false;
|
||||
PR_Unlock(gWatchdogLock);
|
||||
PR_Lock(sr->watchdogLock);
|
||||
sr->watchdogHasTimeout = false;
|
||||
PR_Unlock(sr->watchdogLock);
|
||||
return true;
|
||||
}
|
||||
|
||||
int64_t interval = int64_t(ceil(t * PRMJ_USEC_PER_SEC));
|
||||
int64_t timeout = PRMJ_Now() + interval;
|
||||
PR_Lock(gWatchdogLock);
|
||||
if (!gWatchdogThread) {
|
||||
MOZ_ASSERT(!gWatchdogHasTimeout);
|
||||
gWatchdogThread = PR_CreateThread(PR_USER_THREAD,
|
||||
PR_Lock(sr->watchdogLock);
|
||||
if (!sr->watchdogThread) {
|
||||
MOZ_ASSERT(!sr->watchdogHasTimeout);
|
||||
sr->watchdogThread = PR_CreateThread(PR_USER_THREAD,
|
||||
WatchdogMain,
|
||||
rt,
|
||||
PR_PRIORITY_NORMAL,
|
||||
PR_GLOBAL_THREAD,
|
||||
PR_JOINABLE_THREAD,
|
||||
0);
|
||||
if (!gWatchdogThread) {
|
||||
PR_Unlock(gWatchdogLock);
|
||||
if (!sr->watchdogThread) {
|
||||
PR_Unlock(sr->watchdogLock);
|
||||
return false;
|
||||
}
|
||||
} else if (!gWatchdogHasTimeout || IsBefore(timeout, gWatchdogTimeout)) {
|
||||
PR_NotifyCondVar(gWatchdogWakeup);
|
||||
} else if (!sr->watchdogHasTimeout || IsBefore(timeout, sr->watchdogTimeout)) {
|
||||
PR_NotifyCondVar(sr->watchdogWakeup);
|
||||
}
|
||||
gWatchdogHasTimeout = true;
|
||||
gWatchdogTimeout = timeout;
|
||||
PR_Unlock(gWatchdogLock);
|
||||
sr->watchdogHasTimeout = true;
|
||||
sr->watchdogTimeout = timeout;
|
||||
PR_Unlock(sr->watchdogLock);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
CancelExecution(JSRuntime* rt)
|
||||
{
|
||||
gServiceInterrupt = true;
|
||||
ShellRuntime* sr = GetShellRuntime(rt);
|
||||
sr->serviceInterrupt = true;
|
||||
JS_RequestInterruptCallback(rt);
|
||||
|
||||
if (!gInterruptFunc.isNull()) {
|
||||
if (!sr->interruptFunc.isNull()) {
|
||||
static const char msg[] = "Script runs for too long, terminating.\n";
|
||||
fputs(msg, stderr);
|
||||
}
|
||||
@ -2827,7 +2893,7 @@ SetTimeoutValue(JSContext* cx, double t)
|
||||
JS_ReportError(cx, "Excessive timeout value");
|
||||
return false;
|
||||
}
|
||||
gTimeoutInterval = t;
|
||||
GetShellRuntime(cx)->timeoutInterval = t;
|
||||
if (!ScheduleWatchdog(cx->runtime(), t)) {
|
||||
JS_ReportError(cx, "Failed to create the watchdog");
|
||||
return false;
|
||||
@ -2838,10 +2904,11 @@ SetTimeoutValue(JSContext* cx, double t)
|
||||
static bool
|
||||
Timeout(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
ShellRuntime* sr = GetShellRuntime(cx);
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
if (args.length() == 0) {
|
||||
args.rval().setNumber(gTimeoutInterval);
|
||||
args.rval().setNumber(sr->timeoutInterval);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2860,7 +2927,7 @@ Timeout(JSContext* cx, unsigned argc, Value* vp)
|
||||
JS_ReportError(cx, "Second argument must be a timeout function");
|
||||
return false;
|
||||
}
|
||||
gInterruptFunc = value;
|
||||
sr->interruptFunc = value;
|
||||
}
|
||||
|
||||
args.rval().setUndefined();
|
||||
@ -2878,7 +2945,7 @@ InterruptIf(JSContext* cx, unsigned argc, Value* vp)
|
||||
}
|
||||
|
||||
if (ToBoolean(args[0])) {
|
||||
gServiceInterrupt = true;
|
||||
GetShellRuntime(cx)->serviceInterrupt = true;
|
||||
JS_RequestInterruptCallback(cx->runtime());
|
||||
}
|
||||
|
||||
@ -2895,7 +2962,7 @@ InvokeInterruptCallbackWrapper(JSContext* cx, unsigned argc, Value* vp)
|
||||
return false;
|
||||
}
|
||||
|
||||
gServiceInterrupt = true;
|
||||
GetShellRuntime(cx)->serviceInterrupt = true;
|
||||
JS_RequestInterruptCallback(cx->runtime());
|
||||
bool interruptRv = CheckForInterrupt(cx);
|
||||
|
||||
@ -2931,7 +2998,7 @@ SetInterruptCallback(JSContext* cx, unsigned argc, Value* vp)
|
||||
JS_ReportError(cx, "Argument must be a function");
|
||||
return false;
|
||||
}
|
||||
gInterruptFunc = value;
|
||||
GetShellRuntime(cx)->interruptFunc = value;
|
||||
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
@ -2940,10 +3007,11 @@ SetInterruptCallback(JSContext* cx, unsigned argc, Value* vp)
|
||||
static bool
|
||||
EnableLastWarning(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
ShellRuntime* sr = GetShellRuntime(cx);
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
gLastWarningEnabled = true;
|
||||
gLastWarning.setNull();
|
||||
sr->lastWarningEnabled = true;
|
||||
sr->lastWarning.setNull();
|
||||
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
@ -2952,10 +3020,11 @@ EnableLastWarning(JSContext* cx, unsigned argc, Value* vp)
|
||||
static bool
|
||||
DisableLastWarning(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
ShellRuntime* sr = GetShellRuntime(cx);
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
gLastWarningEnabled = false;
|
||||
gLastWarning.setNull();
|
||||
sr->lastWarningEnabled = false;
|
||||
sr->lastWarning.setNull();
|
||||
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
@ -2964,31 +3033,33 @@ DisableLastWarning(JSContext* cx, unsigned argc, Value* vp)
|
||||
static bool
|
||||
GetLastWarning(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
ShellRuntime* sr = GetShellRuntime(cx);
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
if (!gLastWarningEnabled) {
|
||||
if (!sr->lastWarningEnabled) {
|
||||
JS_ReportError(cx, "Call enableLastWarning first.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!JS_WrapValue(cx, &gLastWarning))
|
||||
if (!JS_WrapValue(cx, &sr->lastWarning))
|
||||
return false;
|
||||
|
||||
args.rval().set(gLastWarning);
|
||||
args.rval().set(sr->lastWarning);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
ClearLastWarning(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
ShellRuntime* sr = GetShellRuntime(cx);
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
if (!gLastWarningEnabled) {
|
||||
if (!sr->lastWarningEnabled) {
|
||||
JS_ReportError(cx, "Call enableLastWarning first.");
|
||||
return false;
|
||||
}
|
||||
|
||||
gLastWarning.setNull();
|
||||
sr->lastWarning.setNull();
|
||||
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
@ -3893,6 +3964,11 @@ static bool
|
||||
SetCachingEnabled(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
if (GetShellRuntime(cx)->isWorker) {
|
||||
JS_ReportError(cx, "Caching is not supported in workers");
|
||||
return false;
|
||||
}
|
||||
|
||||
jsCachingEnabled = ToBoolean(args.get(0));
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
@ -5142,7 +5218,7 @@ CreateLastWarningObject(JSContext* cx, JSErrorReport* report)
|
||||
if (!DefineProperty(cx, warningObj, cx->names().columnNumber, columnVal))
|
||||
return false;
|
||||
|
||||
gLastWarning.setObject(*warningObj);
|
||||
GetShellRuntime(cx)->lastWarning.setObject(*warningObj);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -5184,7 +5260,9 @@ PrintStackTrace(JSContext* cx, HandleValue exn)
|
||||
void
|
||||
js::shell::my_ErrorReporter(JSContext* cx, const char* message, JSErrorReport* report)
|
||||
{
|
||||
if (report && JSREPORT_IS_WARNING(report->flags) && gLastWarningEnabled) {
|
||||
ShellRuntime* sr = GetShellRuntime(cx);
|
||||
|
||||
if (report && JSREPORT_IS_WARNING(report->flags) && sr->lastWarningEnabled) {
|
||||
JS::AutoSaveExceptionState savedExc(cx);
|
||||
if (!CreateLastWarningObject(cx, report)) {
|
||||
fputs("Unhandled error happened while creating last warning object.\n", gOutFile);
|
||||
@ -5198,7 +5276,7 @@ js::shell::my_ErrorReporter(JSContext* cx, const char* message, JSErrorReport* r
|
||||
if (JS_IsExceptionPending(cx))
|
||||
(void) JS_GetPendingException(cx, &exn);
|
||||
|
||||
gGotError = PrintError(cx, gErrFile, message, report, reportWarnings);
|
||||
sr->gotError = PrintError(cx, gErrFile, message, report, reportWarnings);
|
||||
if (!exn.isUndefined()) {
|
||||
JS::AutoSaveExceptionState savedExc(cx);
|
||||
if (!PrintStackTrace(cx, exn))
|
||||
@ -5207,11 +5285,10 @@ js::shell::my_ErrorReporter(JSContext* cx, const char* message, JSErrorReport* r
|
||||
}
|
||||
|
||||
if (report->exnType != JSEXN_NONE && !JSREPORT_IS_WARNING(report->flags)) {
|
||||
if (report->errorNumber == JSMSG_OUT_OF_MEMORY) {
|
||||
gExitCode = EXITCODE_OUT_OF_MEMORY;
|
||||
} else {
|
||||
gExitCode = EXITCODE_RUNTIME_ERROR;
|
||||
}
|
||||
if (report->errorNumber == JSMSG_OUT_OF_MEMORY)
|
||||
sr->exitCode = EXITCODE_OUT_OF_MEMORY;
|
||||
else
|
||||
sr->exitCode = EXITCODE_RUNTIME_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
@ -5222,7 +5299,7 @@ my_OOMCallback(JSContext* cx, void* data)
|
||||
// memory", which may or may not be caught. Otherwise the engine will just
|
||||
// unwind and return null/false, with no exception set.
|
||||
if (!JS_IsRunning(cx))
|
||||
gGotError = true;
|
||||
GetShellRuntime(cx)->gotError = true;
|
||||
}
|
||||
|
||||
static bool
|
||||
@ -5880,6 +5957,8 @@ OptionFailure(const char* option, const char* str)
|
||||
static int
|
||||
ProcessArgs(JSContext* cx, OptionParser* op)
|
||||
{
|
||||
ShellRuntime* sr = GetShellRuntime(cx);
|
||||
|
||||
if (op->getBoolOption('s'))
|
||||
JS::RuntimeOptionsRef(cx).toggleExtraWarnings();
|
||||
|
||||
@ -5892,7 +5971,7 @@ ProcessArgs(JSContext* cx, OptionParser* op)
|
||||
|
||||
if (filePaths.empty() && codeChunks.empty() && !op->getStringArg("script")) {
|
||||
Process(cx, nullptr, true); /* Interactive. */
|
||||
return gExitCode;
|
||||
return sr->exitCode;
|
||||
}
|
||||
|
||||
while (!filePaths.empty() || !codeChunks.empty()) {
|
||||
@ -5901,8 +5980,8 @@ ProcessArgs(JSContext* cx, OptionParser* op)
|
||||
if (fpArgno < ccArgno) {
|
||||
char* path = filePaths.front();
|
||||
Process(cx, path, false);
|
||||
if (gExitCode)
|
||||
return gExitCode;
|
||||
if (sr->exitCode)
|
||||
return sr->exitCode;
|
||||
filePaths.popFront();
|
||||
} else {
|
||||
const char* code = codeChunks.front();
|
||||
@ -5910,27 +5989,27 @@ ProcessArgs(JSContext* cx, OptionParser* op)
|
||||
JS::CompileOptions opts(cx);
|
||||
opts.setFileAndLine("-e", 1);
|
||||
if (!JS::Evaluate(cx, opts, code, strlen(code), &rval))
|
||||
return gExitCode ? gExitCode : EXITCODE_RUNTIME_ERROR;
|
||||
return sr->exitCode ? sr->exitCode : EXITCODE_RUNTIME_ERROR;
|
||||
codeChunks.popFront();
|
||||
if (gQuitting)
|
||||
if (sr->quitting)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (gQuitting)
|
||||
return gExitCode ? gExitCode : EXIT_SUCCESS;
|
||||
if (sr->quitting)
|
||||
return sr->exitCode ? sr->exitCode : EXIT_SUCCESS;
|
||||
|
||||
/* The |script| argument is processed after all options. */
|
||||
if (const char* path = op->getStringArg("script")) {
|
||||
Process(cx, path, false);
|
||||
if (gExitCode)
|
||||
return gExitCode;
|
||||
if (sr->exitCode)
|
||||
return sr->exitCode;
|
||||
}
|
||||
|
||||
if (op->getBoolOption('i'))
|
||||
Process(cx, nullptr, true);
|
||||
|
||||
return gExitCode ? gExitCode : EXIT_SUCCESS;
|
||||
return sr->exitCode ? sr->exitCode : EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static bool
|
||||
@ -6530,13 +6609,18 @@ main(int argc, char** argv, char** envp)
|
||||
if (!rt)
|
||||
return 1;
|
||||
|
||||
mozilla::UniquePtr<ShellRuntime> sr = MakeUnique<ShellRuntime>();
|
||||
if (!sr)
|
||||
return 1;
|
||||
|
||||
JS_SetRuntimePrivate(rt, sr.get());
|
||||
JS_SetErrorReporter(rt, my_ErrorReporter);
|
||||
JS::SetOutOfMemoryCallback(rt, my_OOMCallback, nullptr);
|
||||
if (!SetRuntimeOptions(rt, op))
|
||||
return 1;
|
||||
|
||||
gInterruptFunc.init(rt, NullValue());
|
||||
gLastWarning.init(rt, NullValue());
|
||||
sr->interruptFunc.init(rt, NullValue());
|
||||
sr->lastWarning.init(rt, NullValue());
|
||||
|
||||
JS_SetGCParameter(rt, JSGC_MAX_BYTES, 0xffffffff);
|
||||
|
||||
@ -6594,7 +6678,7 @@ main(int argc, char** argv, char** envp)
|
||||
|
||||
DestroyContext(cx, true);
|
||||
|
||||
KillWatchdog();
|
||||
KillWatchdog(rt);
|
||||
|
||||
MOZ_ASSERT_IF(!CanUseExtraThreads(), workerThreads.empty());
|
||||
for (size_t i = 0; i < workerThreads.length(); i++)
|
||||
|
Loading…
Reference in New Issue
Block a user