Bug 1212349 - Encapsulate the shell's per-runtime state r=jandem

This commit is contained in:
Jon Coppeard 2015-10-08 17:48:53 +01:00
parent db67be8145
commit 6c9c84a8e4

View File

@ -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++)