michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * JS execution context. michael@0: */ michael@0: michael@0: #include "jscntxtinlines.h" michael@0: michael@0: #include "mozilla/ArrayUtils.h" michael@0: #include "mozilla/DebugOnly.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #ifdef ANDROID michael@0: # include michael@0: # include michael@0: # include michael@0: #endif // ANDROID michael@0: michael@0: #include "jsatom.h" michael@0: #include "jscompartment.h" michael@0: #include "jsexn.h" michael@0: #include "jsfun.h" michael@0: #include "jsgc.h" michael@0: #include "jsiter.h" michael@0: #include "jsobj.h" michael@0: #include "jsopcode.h" michael@0: #include "jsprf.h" michael@0: #include "jspubtd.h" michael@0: #include "jsscript.h" michael@0: #include "jsstr.h" michael@0: #include "jstypes.h" michael@0: #include "jswatchpoint.h" michael@0: #include "jsworkers.h" michael@0: michael@0: #include "gc/Marking.h" michael@0: #ifdef JS_ION michael@0: #include "jit/Ion.h" michael@0: #endif michael@0: #include "js/CharacterEncoding.h" michael@0: #include "js/OldDebugAPI.h" michael@0: #include "vm/Shape.h" michael@0: #include "yarr/BumpPointerAllocator.h" michael@0: michael@0: #include "jsobjinlines.h" michael@0: #include "jsscriptinlines.h" michael@0: michael@0: #include "vm/Stack-inl.h" michael@0: michael@0: using namespace js; michael@0: using namespace js::gc; michael@0: michael@0: using mozilla::DebugOnly; michael@0: using mozilla::PodArrayZero; michael@0: using mozilla::PodZero; michael@0: using mozilla::PointerRangeSize; michael@0: michael@0: bool michael@0: js::AutoCycleDetector::init() michael@0: { michael@0: ObjectSet &set = cx->cycleDetectorSet; michael@0: hashsetAddPointer = set.lookupForAdd(obj); michael@0: if (!hashsetAddPointer) { michael@0: if (!set.add(hashsetAddPointer, obj)) michael@0: return false; michael@0: cyclic = false; michael@0: hashsetGenerationAtInit = set.generation(); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: js::AutoCycleDetector::~AutoCycleDetector() michael@0: { michael@0: if (!cyclic) { michael@0: if (hashsetGenerationAtInit == cx->cycleDetectorSet.generation()) michael@0: cx->cycleDetectorSet.remove(hashsetAddPointer); michael@0: else michael@0: cx->cycleDetectorSet.remove(obj); michael@0: } michael@0: } michael@0: michael@0: void michael@0: js::TraceCycleDetectionSet(JSTracer *trc, js::ObjectSet &set) michael@0: { michael@0: for (js::ObjectSet::Enum e(set); !e.empty(); e.popFront()) { michael@0: JSObject *prior = e.front(); michael@0: MarkObjectRoot(trc, const_cast(&e.front()), "cycle detector table entry"); michael@0: if (prior != e.front()) michael@0: e.rekeyFront(e.front()); michael@0: } michael@0: } michael@0: michael@0: void michael@0: JSCompartment::sweepCallsiteClones() michael@0: { michael@0: if (callsiteClones.initialized()) { michael@0: for (CallsiteCloneTable::Enum e(callsiteClones); !e.empty(); e.popFront()) { michael@0: CallsiteCloneKey key = e.front().key(); michael@0: JSFunction *fun = e.front().value(); michael@0: if (!IsScriptMarked(&key.script) || !IsObjectMarked(&fun)) michael@0: e.removeFront(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: JSFunction * michael@0: js::ExistingCloneFunctionAtCallsite(const CallsiteCloneTable &table, JSFunction *fun, michael@0: JSScript *script, jsbytecode *pc) michael@0: { michael@0: JS_ASSERT(fun->nonLazyScript()->shouldCloneAtCallsite()); michael@0: JS_ASSERT(!fun->nonLazyScript()->enclosingStaticScope()); michael@0: JS_ASSERT(types::UseNewTypeForClone(fun)); michael@0: michael@0: /* michael@0: * If we start allocating function objects in the nursery, then the callsite michael@0: * clone table will need a postbarrier. michael@0: */ michael@0: JS_ASSERT(fun->isTenured()); michael@0: michael@0: if (!table.initialized()) michael@0: return nullptr; michael@0: michael@0: CallsiteCloneTable::Ptr p = table.readonlyThreadsafeLookup(CallsiteCloneKey(fun, script, script->pcToOffset(pc))); michael@0: if (p) michael@0: return p->value(); michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: JSFunction * michael@0: js::CloneFunctionAtCallsite(JSContext *cx, HandleFunction fun, HandleScript script, jsbytecode *pc) michael@0: { michael@0: if (JSFunction *clone = ExistingCloneFunctionAtCallsite(cx->compartment()->callsiteClones, fun, script, pc)) michael@0: return clone; michael@0: michael@0: RootedObject parent(cx, fun->environment()); michael@0: JSFunction *clone = CloneFunctionObject(cx, fun, parent); michael@0: if (!clone) michael@0: return nullptr; michael@0: michael@0: /* michael@0: * Store a link back to the original for function.caller and avoid cloning michael@0: * clones. michael@0: */ michael@0: clone->nonLazyScript()->setIsCallsiteClone(fun); michael@0: michael@0: typedef CallsiteCloneKey Key; michael@0: typedef CallsiteCloneTable Table; michael@0: michael@0: Table &table = cx->compartment()->callsiteClones; michael@0: if (!table.initialized() && !table.init()) michael@0: return nullptr; michael@0: michael@0: if (!table.putNew(Key(fun, script, script->pcToOffset(pc)), clone)) michael@0: return nullptr; michael@0: michael@0: return clone; michael@0: } michael@0: michael@0: JSContext * michael@0: js::NewContext(JSRuntime *rt, size_t stackChunkSize) michael@0: { michael@0: JS_AbortIfWrongThread(rt); michael@0: michael@0: JSContext *cx = js_new(rt); michael@0: if (!cx) michael@0: return nullptr; michael@0: michael@0: if (!cx->cycleDetectorSet.init()) { michael@0: js_delete(cx); michael@0: return nullptr; michael@0: } michael@0: michael@0: /* michael@0: * Here the GC lock is still held after js_InitContextThreadAndLockGC took it and michael@0: * the GC is not running on another thread. michael@0: */ michael@0: rt->contextList.insertBack(cx); michael@0: michael@0: /* michael@0: * If cx is the first context on this runtime, initialize well-known atoms, michael@0: * keywords, numbers, strings and self-hosted scripts. If one of these michael@0: * steps should fail, the runtime will be left in a partially initialized michael@0: * state, with zeroes and nulls stored in the default-initialized remainder michael@0: * of the struct. michael@0: */ michael@0: if (!rt->haveCreatedContext) { michael@0: #ifdef JS_THREADSAFE michael@0: JS_BeginRequest(cx); michael@0: #endif michael@0: bool ok = rt->initializeAtoms(cx); michael@0: if (ok) michael@0: ok = rt->initSelfHosting(cx); michael@0: michael@0: if (ok && !rt->parentRuntime) michael@0: ok = rt->transformToPermanentAtoms(); michael@0: michael@0: #ifdef JS_THREADSAFE michael@0: JS_EndRequest(cx); michael@0: #endif michael@0: if (!ok) { michael@0: DestroyContext(cx, DCM_NEW_FAILED); michael@0: return nullptr; michael@0: } michael@0: michael@0: rt->haveCreatedContext = true; michael@0: } michael@0: michael@0: JSContextCallback cxCallback = rt->cxCallback; michael@0: if (cxCallback && !cxCallback(cx, JSCONTEXT_NEW, rt->cxCallbackData)) { michael@0: DestroyContext(cx, DCM_NEW_FAILED); michael@0: return nullptr; michael@0: } michael@0: michael@0: return cx; michael@0: } michael@0: michael@0: void michael@0: js::DestroyContext(JSContext *cx, DestroyContextMode mode) michael@0: { michael@0: JSRuntime *rt = cx->runtime(); michael@0: JS_AbortIfWrongThread(rt); michael@0: michael@0: #ifdef JS_THREADSAFE michael@0: if (cx->outstandingRequests != 0) michael@0: MOZ_CRASH(); michael@0: #endif michael@0: michael@0: #if defined(JSGC_USE_EXACT_ROOTING) && defined(DEBUG) michael@0: for (int i = 0; i < THING_ROOT_LIMIT; ++i) michael@0: JS_ASSERT(cx->thingGCRooters[i] == nullptr); michael@0: #endif michael@0: michael@0: if (mode != DCM_NEW_FAILED) { michael@0: if (JSContextCallback cxCallback = rt->cxCallback) { michael@0: /* michael@0: * JSCONTEXT_DESTROY callback is not allowed to fail and must michael@0: * return true. michael@0: */ michael@0: JS_ALWAYS_TRUE(cxCallback(cx, JSCONTEXT_DESTROY, michael@0: rt->cxCallbackData)); michael@0: } michael@0: } michael@0: michael@0: cx->remove(); michael@0: bool last = !rt->hasContexts(); michael@0: if (last) { michael@0: /* michael@0: * Dump remaining type inference results while we still have a context. michael@0: * This printing depends on atoms still existing. michael@0: */ michael@0: for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) michael@0: c->types.print(cx, false); michael@0: } michael@0: if (mode == DCM_FORCE_GC) { michael@0: JS_ASSERT(!rt->isHeapBusy()); michael@0: JS::PrepareForFullGC(rt); michael@0: GC(rt, GC_NORMAL, JS::gcreason::DESTROY_CONTEXT); michael@0: } michael@0: js_delete_poison(cx); michael@0: } michael@0: michael@0: bool michael@0: AutoResolving::alreadyStartedSlow() const michael@0: { michael@0: JS_ASSERT(link); michael@0: AutoResolving *cursor = link; michael@0: do { michael@0: JS_ASSERT(this != cursor); michael@0: if (object.get() == cursor->object && id.get() == cursor->id && kind == cursor->kind) michael@0: return true; michael@0: } while (!!(cursor = cursor->link)); michael@0: return false; michael@0: } michael@0: michael@0: static void michael@0: ReportError(JSContext *cx, const char *message, JSErrorReport *reportp, michael@0: JSErrorCallback callback, void *userRef) michael@0: { michael@0: /* michael@0: * Check the error report, and set a JavaScript-catchable exception michael@0: * if the error is defined to have an associated exception. If an michael@0: * exception is thrown, then the JSREPORT_EXCEPTION flag will be set michael@0: * on the error report, and exception-aware hosts should ignore it. michael@0: */ michael@0: JS_ASSERT(reportp); michael@0: if ((!callback || callback == js_GetErrorMessage) && michael@0: reportp->errorNumber == JSMSG_UNCAUGHT_EXCEPTION) michael@0: { michael@0: reportp->flags |= JSREPORT_EXCEPTION; michael@0: } michael@0: michael@0: /* michael@0: * Call the error reporter only if an exception wasn't raised. michael@0: * michael@0: * If an exception was raised, then we call the debugErrorHook michael@0: * (if present) to give it a chance to see the error before it michael@0: * propagates out of scope. This is needed for compatibility michael@0: * with the old scheme. michael@0: */ michael@0: if (!JS_IsRunning(cx) || !js_ErrorToException(cx, message, reportp, callback, userRef)) { michael@0: if (message) michael@0: CallErrorReporter(cx, message, reportp); michael@0: } else if (JSDebugErrorHook hook = cx->runtime()->debugHooks.debugErrorHook) { michael@0: /* michael@0: * If we've already chewed up all the C stack, don't call into the michael@0: * error reporter since this may trigger an infinite recursion where michael@0: * the reporter triggers an over-recursion. michael@0: */ michael@0: int stackDummy; michael@0: if (!JS_CHECK_STACK_SIZE(GetNativeStackLimit(cx), &stackDummy)) michael@0: return; michael@0: michael@0: if (cx->errorReporter) michael@0: hook(cx, message, reportp, cx->runtime()->debugHooks.debugErrorHookData); michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * The given JSErrorReport object have been zeroed and must not outlive michael@0: * cx->fp() (otherwise report->originPrincipals may become invalid). michael@0: */ michael@0: static void michael@0: PopulateReportBlame(JSContext *cx, JSErrorReport *report) michael@0: { michael@0: /* michael@0: * Walk stack until we find a frame that is associated with a non-builtin michael@0: * rather than a builtin frame. michael@0: */ michael@0: NonBuiltinFrameIter iter(cx); michael@0: if (iter.done()) michael@0: return; michael@0: michael@0: report->filename = iter.scriptFilename(); michael@0: report->lineno = iter.computeLine(&report->column); michael@0: report->originPrincipals = iter.originPrincipals(); michael@0: } michael@0: michael@0: /* michael@0: * Since memory has been exhausted, avoid the normal error-handling path which michael@0: * allocates an error object, report and callstack. If code is running, simply michael@0: * throw the static atom "out of memory". If code is not running, call the michael@0: * error reporter directly. michael@0: * michael@0: * Furthermore, callers of js_ReportOutOfMemory (viz., malloc) assume a GC does michael@0: * not occur, so GC must be avoided or suppressed. michael@0: */ michael@0: void michael@0: js_ReportOutOfMemory(ThreadSafeContext *cxArg) michael@0: { michael@0: #ifdef JS_MORE_DETERMINISTIC michael@0: /* michael@0: * OOMs are non-deterministic, especially across different execution modes michael@0: * (e.g. interpreter vs JIT). In more-deterministic builds, print to stderr michael@0: * so that the fuzzers can detect this. michael@0: */ michael@0: fprintf(stderr, "js_ReportOutOfMemory called\n"); michael@0: #endif michael@0: michael@0: if (cxArg->isForkJoinContext()) { michael@0: cxArg->asForkJoinContext()->setPendingAbortFatal(ParallelBailoutOutOfMemory); michael@0: return; michael@0: } michael@0: michael@0: if (!cxArg->isJSContext()) michael@0: return; michael@0: michael@0: JSContext *cx = cxArg->asJSContext(); michael@0: cx->runtime()->hadOutOfMemory = true; michael@0: michael@0: /* Report the oom. */ michael@0: if (JS::OutOfMemoryCallback oomCallback = cx->runtime()->oomCallback) { michael@0: AutoSuppressGC suppressGC(cx); michael@0: oomCallback(cx); michael@0: } michael@0: michael@0: if (JS_IsRunning(cx)) { michael@0: cx->setPendingException(StringValue(cx->names().outOfMemory)); michael@0: return; michael@0: } michael@0: michael@0: /* Get the message for this error, but we don't expand any arguments. */ michael@0: const JSErrorFormatString *efs = michael@0: js_GetLocalizedErrorMessage(cx, nullptr, nullptr, JSMSG_OUT_OF_MEMORY); michael@0: const char *msg = efs ? efs->format : "Out of memory"; michael@0: michael@0: /* Fill out the report, but don't do anything that requires allocation. */ michael@0: JSErrorReport report; michael@0: PodZero(&report); michael@0: report.flags = JSREPORT_ERROR; michael@0: report.errorNumber = JSMSG_OUT_OF_MEMORY; michael@0: PopulateReportBlame(cx, &report); michael@0: michael@0: /* Report the error. */ michael@0: if (JSErrorReporter onError = cx->errorReporter) { michael@0: AutoSuppressGC suppressGC(cx); michael@0: onError(cx, msg, &report); michael@0: } michael@0: michael@0: /* michael@0: * We would like to enforce the invariant that any exception reported michael@0: * during an OOM situation does not require wrapping. Besides avoiding michael@0: * allocation when memory is low, this reduces the number of places where michael@0: * we might need to GC. michael@0: * michael@0: * When JS code is running, we set the pending exception to an atom, which michael@0: * does not need wrapping. If no JS code is running, no exception should be michael@0: * set at all. michael@0: */ michael@0: JS_ASSERT(!cx->isExceptionPending()); michael@0: } michael@0: michael@0: JS_FRIEND_API(void) michael@0: js_ReportOverRecursed(JSContext *maybecx) michael@0: { michael@0: #ifdef JS_MORE_DETERMINISTIC michael@0: /* michael@0: * We cannot make stack depth deterministic across different michael@0: * implementations (e.g. JIT vs. interpreter will differ in michael@0: * their maximum stack depth). michael@0: * However, we can detect externally when we hit the maximum michael@0: * stack depth which is useful for external testing programs michael@0: * like fuzzers. michael@0: */ michael@0: fprintf(stderr, "js_ReportOverRecursed called\n"); michael@0: #endif michael@0: if (maybecx) michael@0: JS_ReportErrorNumber(maybecx, js_GetErrorMessage, nullptr, JSMSG_OVER_RECURSED); michael@0: } michael@0: michael@0: void michael@0: js_ReportOverRecursed(ThreadSafeContext *cx) michael@0: { michael@0: if (cx->isJSContext()) michael@0: js_ReportOverRecursed(cx->asJSContext()); michael@0: else if (cx->isExclusiveContext()) michael@0: cx->asExclusiveContext()->addPendingOverRecursed(); michael@0: } michael@0: michael@0: void michael@0: js_ReportAllocationOverflow(ThreadSafeContext *cxArg) michael@0: { michael@0: if (!cxArg) michael@0: return; michael@0: michael@0: if (cxArg->isForkJoinContext()) { michael@0: cxArg->asForkJoinContext()->setPendingAbortFatal(ParallelBailoutOutOfMemory); michael@0: return; michael@0: } michael@0: michael@0: if (!cxArg->isJSContext()) michael@0: return; michael@0: JSContext *cx = cxArg->asJSContext(); michael@0: michael@0: AutoSuppressGC suppressGC(cx); michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_ALLOC_OVERFLOW); michael@0: } michael@0: michael@0: /* michael@0: * Given flags and the state of cx, decide whether we should report an michael@0: * error, a warning, or just continue execution normally. Return michael@0: * true if we should continue normally, without reporting anything; michael@0: * otherwise, adjust *flags as appropriate and return false. michael@0: */ michael@0: static bool michael@0: checkReportFlags(JSContext *cx, unsigned *flags) michael@0: { michael@0: if (JSREPORT_IS_STRICT_MODE_ERROR(*flags)) { michael@0: /* michael@0: * Error in strict code; warning with extra warnings option; okay michael@0: * otherwise. We assume that if the top frame is a native, then it is michael@0: * strict if the nearest scripted frame is strict, see bug 536306. michael@0: */ michael@0: JSScript *script = cx->currentScript(); michael@0: if (script && script->strict()) michael@0: *flags &= ~JSREPORT_WARNING; michael@0: else if (cx->options().extraWarnings()) michael@0: *flags |= JSREPORT_WARNING; michael@0: else michael@0: return true; michael@0: } else if (JSREPORT_IS_STRICT(*flags)) { michael@0: /* Warning/error only when JSOPTION_STRICT is set. */ michael@0: if (!cx->options().extraWarnings()) michael@0: return true; michael@0: } michael@0: michael@0: /* Warnings become errors when JSOPTION_WERROR is set. */ michael@0: if (JSREPORT_IS_WARNING(*flags) && cx->options().werror()) michael@0: *flags &= ~JSREPORT_WARNING; michael@0: michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: js_ReportErrorVA(JSContext *cx, unsigned flags, const char *format, va_list ap) michael@0: { michael@0: char *message; michael@0: jschar *ucmessage; michael@0: size_t messagelen; michael@0: JSErrorReport report; michael@0: bool warning; michael@0: michael@0: if (checkReportFlags(cx, &flags)) michael@0: return true; michael@0: michael@0: message = JS_vsmprintf(format, ap); michael@0: if (!message) michael@0: return false; michael@0: messagelen = strlen(message); michael@0: michael@0: PodZero(&report); michael@0: report.flags = flags; michael@0: report.errorNumber = JSMSG_USER_DEFINED_ERROR; michael@0: report.ucmessage = ucmessage = InflateString(cx, message, &messagelen); michael@0: PopulateReportBlame(cx, &report); michael@0: michael@0: warning = JSREPORT_IS_WARNING(report.flags); michael@0: michael@0: ReportError(cx, message, &report, nullptr, nullptr); michael@0: js_free(message); michael@0: js_free(ucmessage); michael@0: return warning; michael@0: } michael@0: michael@0: /* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */ michael@0: void michael@0: js::ReportUsageError(JSContext *cx, HandleObject callee, const char *msg) michael@0: { michael@0: const char *usageStr = "usage"; michael@0: PropertyName *usageAtom = Atomize(cx, usageStr, strlen(usageStr))->asPropertyName(); michael@0: RootedId id(cx, NameToId(usageAtom)); michael@0: DebugOnly shape = static_cast(callee->nativeLookup(cx, id)); michael@0: JS_ASSERT(!shape->configurable()); michael@0: JS_ASSERT(!shape->writable()); michael@0: JS_ASSERT(shape->hasDefaultGetter()); michael@0: michael@0: RootedValue usage(cx); michael@0: if (!JS_LookupProperty(cx, callee, "usage", &usage)) michael@0: return; michael@0: michael@0: if (JSVAL_IS_VOID(usage)) { michael@0: JS_ReportError(cx, "%s", msg); michael@0: } else { michael@0: JSString *str = JSVAL_TO_STRING(usage); michael@0: JS::Anchor a_str(str); michael@0: const jschar *chars = JS_GetStringCharsZ(cx, str); michael@0: if (!chars) michael@0: return; michael@0: JS_ReportError(cx, "%s. Usage: %hs", msg, chars); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: js::PrintError(JSContext *cx, FILE *file, const char *message, JSErrorReport *report, michael@0: bool reportWarnings) michael@0: { michael@0: if (!report) { michael@0: fprintf(file, "%s\n", message); michael@0: fflush(file); michael@0: return false; michael@0: } michael@0: michael@0: /* Conditionally ignore reported warnings. */ michael@0: if (JSREPORT_IS_WARNING(report->flags) && !reportWarnings) michael@0: return false; michael@0: michael@0: char *prefix = nullptr; michael@0: if (report->filename) michael@0: prefix = JS_smprintf("%s:", report->filename); michael@0: if (report->lineno) { michael@0: char *tmp = prefix; michael@0: prefix = JS_smprintf("%s%u:%u ", tmp ? tmp : "", report->lineno, report->column); michael@0: JS_free(cx, tmp); michael@0: } michael@0: if (JSREPORT_IS_WARNING(report->flags)) { michael@0: char *tmp = prefix; michael@0: prefix = JS_smprintf("%s%swarning: ", michael@0: tmp ? tmp : "", michael@0: JSREPORT_IS_STRICT(report->flags) ? "strict " : ""); michael@0: JS_free(cx, tmp); michael@0: } michael@0: michael@0: /* embedded newlines -- argh! */ michael@0: const char *ctmp; michael@0: while ((ctmp = strchr(message, '\n')) != 0) { michael@0: ctmp++; michael@0: if (prefix) michael@0: fputs(prefix, file); michael@0: fwrite(message, 1, ctmp - message, file); michael@0: message = ctmp; michael@0: } michael@0: michael@0: /* If there were no filename or lineno, the prefix might be empty */ michael@0: if (prefix) michael@0: fputs(prefix, file); michael@0: fputs(message, file); michael@0: michael@0: if (report->linebuf) { michael@0: /* report->linebuf usually ends with a newline. */ michael@0: int n = strlen(report->linebuf); michael@0: fprintf(file, ":\n%s%s%s%s", michael@0: prefix, michael@0: report->linebuf, michael@0: (n > 0 && report->linebuf[n-1] == '\n') ? "" : "\n", michael@0: prefix); michael@0: n = report->tokenptr - report->linebuf; michael@0: for (int i = 0, j = 0; i < n; i++) { michael@0: if (report->linebuf[i] == '\t') { michael@0: for (int k = (j + 8) & ~7; j < k; j++) { michael@0: fputc('.', file); michael@0: } michael@0: continue; michael@0: } michael@0: fputc('.', file); michael@0: j++; michael@0: } michael@0: fputc('^', file); michael@0: } michael@0: fputc('\n', file); michael@0: fflush(file); michael@0: JS_free(cx, prefix); michael@0: return true; michael@0: } michael@0: michael@0: char * michael@0: js_strdup(ExclusiveContext *cx, const char *s) michael@0: { michael@0: size_t n = strlen(s) + 1; michael@0: void *p = cx->malloc_(n); michael@0: if (!p) michael@0: return nullptr; michael@0: return (char *)js_memcpy(p, s, n); michael@0: } michael@0: michael@0: /* michael@0: * The arguments from ap need to be packaged up into an array and stored michael@0: * into the report struct. michael@0: * michael@0: * The format string addressed by the error number may contain operands michael@0: * identified by the format {N}, where N is a decimal digit. Each of these michael@0: * is to be replaced by the Nth argument from the va_list. The complete michael@0: * message is placed into reportp->ucmessage converted to a JSString. michael@0: * michael@0: * Returns true if the expansion succeeds (can fail if out of memory). michael@0: */ michael@0: bool michael@0: js_ExpandErrorArguments(ExclusiveContext *cx, JSErrorCallback callback, michael@0: void *userRef, const unsigned errorNumber, michael@0: char **messagep, JSErrorReport *reportp, michael@0: ErrorArgumentsType argumentsType, va_list ap) michael@0: { michael@0: const JSErrorFormatString *efs; michael@0: int i; michael@0: int argCount; michael@0: bool messageArgsPassed = !!reportp->messageArgs; michael@0: michael@0: *messagep = nullptr; michael@0: michael@0: /* Most calls supply js_GetErrorMessage; if this is so, assume nullptr. */ michael@0: if (!callback || callback == js_GetErrorMessage) michael@0: efs = js_GetLocalizedErrorMessage(cx, userRef, nullptr, errorNumber); michael@0: else michael@0: efs = callback(userRef, nullptr, errorNumber); michael@0: if (efs) { michael@0: reportp->exnType = efs->exnType; michael@0: michael@0: size_t totalArgsLength = 0; michael@0: size_t argLengths[10]; /* only {0} thru {9} supported */ michael@0: argCount = efs->argCount; michael@0: JS_ASSERT(argCount <= 10); michael@0: if (argCount > 0) { michael@0: /* michael@0: * Gather the arguments into an array, and accumulate michael@0: * their sizes. We allocate 1 more than necessary and michael@0: * null it out to act as the caboose when we free the michael@0: * pointers later. michael@0: */ michael@0: if (messageArgsPassed) { michael@0: JS_ASSERT(!reportp->messageArgs[argCount]); michael@0: } else { michael@0: reportp->messageArgs = cx->pod_malloc(argCount + 1); michael@0: if (!reportp->messageArgs) michael@0: return false; michael@0: /* nullptr-terminate for easy copying. */ michael@0: reportp->messageArgs[argCount] = nullptr; michael@0: } michael@0: for (i = 0; i < argCount; i++) { michael@0: if (messageArgsPassed) { michael@0: /* Do nothing. */ michael@0: } else if (argumentsType == ArgumentsAreASCII) { michael@0: char *charArg = va_arg(ap, char *); michael@0: size_t charArgLength = strlen(charArg); michael@0: reportp->messageArgs[i] = InflateString(cx, charArg, &charArgLength); michael@0: if (!reportp->messageArgs[i]) michael@0: goto error; michael@0: } else { michael@0: reportp->messageArgs[i] = va_arg(ap, jschar *); michael@0: } michael@0: argLengths[i] = js_strlen(reportp->messageArgs[i]); michael@0: totalArgsLength += argLengths[i]; michael@0: } michael@0: } michael@0: /* michael@0: * Parse the error format, substituting the argument X michael@0: * for {X} in the format. michael@0: */ michael@0: if (argCount > 0) { michael@0: if (efs->format) { michael@0: jschar *buffer, *fmt, *out; michael@0: int expandedArgs = 0; michael@0: size_t expandedLength; michael@0: size_t len = strlen(efs->format); michael@0: michael@0: buffer = fmt = InflateString(cx, efs->format, &len); michael@0: if (!buffer) michael@0: goto error; michael@0: expandedLength = len michael@0: - (3 * argCount) /* exclude the {n} */ michael@0: + totalArgsLength; michael@0: michael@0: /* michael@0: * Note - the above calculation assumes that each argument michael@0: * is used once and only once in the expansion !!! michael@0: */ michael@0: reportp->ucmessage = out = cx->pod_malloc(expandedLength + 1); michael@0: if (!out) { michael@0: js_free(buffer); michael@0: goto error; michael@0: } michael@0: while (*fmt) { michael@0: if (*fmt == '{') { michael@0: if (isdigit(fmt[1])) { michael@0: int d = JS7_UNDEC(fmt[1]); michael@0: JS_ASSERT(d < argCount); michael@0: js_strncpy(out, reportp->messageArgs[d], michael@0: argLengths[d]); michael@0: out += argLengths[d]; michael@0: fmt += 3; michael@0: expandedArgs++; michael@0: continue; michael@0: } michael@0: } michael@0: *out++ = *fmt++; michael@0: } michael@0: JS_ASSERT(expandedArgs == argCount); michael@0: *out = 0; michael@0: js_free(buffer); michael@0: TwoByteChars ucmsg(reportp->ucmessage, michael@0: PointerRangeSize(static_cast(reportp->ucmessage), michael@0: static_cast(out))); michael@0: *messagep = LossyTwoByteCharsToNewLatin1CharsZ(cx, ucmsg).c_str(); michael@0: if (!*messagep) michael@0: goto error; michael@0: } michael@0: } else { michael@0: /* Non-null messageArgs should have at least one non-null arg. */ michael@0: JS_ASSERT(!reportp->messageArgs); michael@0: /* michael@0: * Zero arguments: the format string (if it exists) is the michael@0: * entire message. michael@0: */ michael@0: if (efs->format) { michael@0: size_t len; michael@0: *messagep = js_strdup(cx, efs->format); michael@0: if (!*messagep) michael@0: goto error; michael@0: len = strlen(*messagep); michael@0: reportp->ucmessage = InflateString(cx, *messagep, &len); michael@0: if (!reportp->ucmessage) michael@0: goto error; michael@0: } michael@0: } michael@0: } michael@0: if (*messagep == nullptr) { michael@0: /* where's the right place for this ??? */ michael@0: const char *defaultErrorMessage michael@0: = "No error message available for error number %d"; michael@0: size_t nbytes = strlen(defaultErrorMessage) + 16; michael@0: *messagep = cx->pod_malloc(nbytes); michael@0: if (!*messagep) michael@0: goto error; michael@0: JS_snprintf(*messagep, nbytes, defaultErrorMessage, errorNumber); michael@0: } michael@0: return true; michael@0: michael@0: error: michael@0: if (!messageArgsPassed && reportp->messageArgs) { michael@0: /* free the arguments only if we allocated them */ michael@0: if (argumentsType == ArgumentsAreASCII) { michael@0: i = 0; michael@0: while (reportp->messageArgs[i]) michael@0: js_free((void *)reportp->messageArgs[i++]); michael@0: } michael@0: js_free((void *)reportp->messageArgs); michael@0: reportp->messageArgs = nullptr; michael@0: } michael@0: if (reportp->ucmessage) { michael@0: js_free((void *)reportp->ucmessage); michael@0: reportp->ucmessage = nullptr; michael@0: } michael@0: if (*messagep) { michael@0: js_free((void *)*messagep); michael@0: *messagep = nullptr; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: js_ReportErrorNumberVA(JSContext *cx, unsigned flags, JSErrorCallback callback, michael@0: void *userRef, const unsigned errorNumber, michael@0: ErrorArgumentsType argumentsType, va_list ap) michael@0: { michael@0: JSErrorReport report; michael@0: char *message; michael@0: bool warning; michael@0: michael@0: if (checkReportFlags(cx, &flags)) michael@0: return true; michael@0: warning = JSREPORT_IS_WARNING(flags); michael@0: michael@0: PodZero(&report); michael@0: report.flags = flags; michael@0: report.errorNumber = errorNumber; michael@0: PopulateReportBlame(cx, &report); michael@0: michael@0: if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber, michael@0: &message, &report, argumentsType, ap)) { michael@0: return false; michael@0: } michael@0: michael@0: ReportError(cx, message, &report, callback, userRef); michael@0: michael@0: js_free(message); michael@0: if (report.messageArgs) { michael@0: /* michael@0: * js_ExpandErrorArguments owns its messageArgs only if it had to michael@0: * inflate the arguments (from regular |char *|s). michael@0: */ michael@0: if (argumentsType == ArgumentsAreASCII) { michael@0: int i = 0; michael@0: while (report.messageArgs[i]) michael@0: js_free((void *)report.messageArgs[i++]); michael@0: } michael@0: js_free((void *)report.messageArgs); michael@0: } michael@0: js_free((void *)report.ucmessage); michael@0: michael@0: return warning; michael@0: } michael@0: michael@0: bool michael@0: js_ReportErrorNumberUCArray(JSContext *cx, unsigned flags, JSErrorCallback callback, michael@0: void *userRef, const unsigned errorNumber, michael@0: const jschar **args) michael@0: { michael@0: if (checkReportFlags(cx, &flags)) michael@0: return true; michael@0: bool warning = JSREPORT_IS_WARNING(flags); michael@0: michael@0: JSErrorReport report; michael@0: PodZero(&report); michael@0: report.flags = flags; michael@0: report.errorNumber = errorNumber; michael@0: PopulateReportBlame(cx, &report); michael@0: report.messageArgs = args; michael@0: michael@0: char *message; michael@0: va_list dummy; michael@0: if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber, michael@0: &message, &report, ArgumentsAreUnicode, dummy)) { michael@0: return false; michael@0: } michael@0: michael@0: ReportError(cx, message, &report, callback, userRef); michael@0: michael@0: js_free(message); michael@0: js_free((void *)report.ucmessage); michael@0: michael@0: return warning; michael@0: } michael@0: michael@0: void michael@0: js::CallErrorReporter(JSContext *cx, const char *message, JSErrorReport *reportp) michael@0: { michael@0: JS_ASSERT(message); michael@0: JS_ASSERT(reportp); michael@0: michael@0: // If debugErrorHook is present, give it a chance to veto sending the error michael@0: // on to the regular ErrorReporter. michael@0: if (cx->errorReporter) { michael@0: JSDebugErrorHook hook = cx->runtime()->debugHooks.debugErrorHook; michael@0: if (hook && !hook(cx, message, reportp, cx->runtime()->debugHooks.debugErrorHookData)) michael@0: return; michael@0: } michael@0: michael@0: if (JSErrorReporter onError = cx->errorReporter) michael@0: onError(cx, message, reportp); michael@0: } michael@0: michael@0: void michael@0: js_ReportIsNotDefined(JSContext *cx, const char *name) michael@0: { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_DEFINED, name); michael@0: } michael@0: michael@0: bool michael@0: js_ReportIsNullOrUndefined(JSContext *cx, int spindex, HandleValue v, michael@0: HandleString fallback) michael@0: { michael@0: char *bytes; michael@0: bool ok; michael@0: michael@0: bytes = DecompileValueGenerator(cx, spindex, v, fallback); michael@0: if (!bytes) michael@0: return false; michael@0: michael@0: if (strcmp(bytes, js_undefined_str) == 0 || michael@0: strcmp(bytes, js_null_str) == 0) { michael@0: ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, michael@0: js_GetErrorMessage, nullptr, michael@0: JSMSG_NO_PROPERTIES, bytes, michael@0: nullptr, nullptr); michael@0: } else if (v.isUndefined()) { michael@0: ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, michael@0: js_GetErrorMessage, nullptr, michael@0: JSMSG_UNEXPECTED_TYPE, bytes, michael@0: js_undefined_str, nullptr); michael@0: } else { michael@0: JS_ASSERT(v.isNull()); michael@0: ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, michael@0: js_GetErrorMessage, nullptr, michael@0: JSMSG_UNEXPECTED_TYPE, bytes, michael@0: js_null_str, nullptr); michael@0: } michael@0: michael@0: js_free(bytes); michael@0: return ok; michael@0: } michael@0: michael@0: void michael@0: js_ReportMissingArg(JSContext *cx, HandleValue v, unsigned arg) michael@0: { michael@0: char argbuf[11]; michael@0: char *bytes; michael@0: RootedAtom atom(cx); michael@0: michael@0: JS_snprintf(argbuf, sizeof argbuf, "%u", arg); michael@0: bytes = nullptr; michael@0: if (IsFunctionObject(v)) { michael@0: atom = v.toObject().as().atom(); michael@0: bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, michael@0: v, atom); michael@0: if (!bytes) michael@0: return; michael@0: } michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, michael@0: JSMSG_MISSING_FUN_ARG, argbuf, michael@0: bytes ? bytes : ""); michael@0: js_free(bytes); michael@0: } michael@0: michael@0: bool michael@0: js_ReportValueErrorFlags(JSContext *cx, unsigned flags, const unsigned errorNumber, michael@0: int spindex, HandleValue v, HandleString fallback, michael@0: const char *arg1, const char *arg2) michael@0: { michael@0: char *bytes; michael@0: bool ok; michael@0: michael@0: JS_ASSERT(js_ErrorFormatString[errorNumber].argCount >= 1); michael@0: JS_ASSERT(js_ErrorFormatString[errorNumber].argCount <= 3); michael@0: bytes = DecompileValueGenerator(cx, spindex, v, fallback); michael@0: if (!bytes) michael@0: return false; michael@0: michael@0: ok = JS_ReportErrorFlagsAndNumber(cx, flags, js_GetErrorMessage, michael@0: nullptr, errorNumber, bytes, arg1, arg2); michael@0: js_free(bytes); michael@0: return ok; michael@0: } michael@0: michael@0: const JSErrorFormatString js_ErrorFormatString[JSErr_Limit] = { michael@0: #define MSG_DEF(name, number, count, exception, format) \ michael@0: { format, count, exception } , michael@0: #include "js.msg" michael@0: #undef MSG_DEF michael@0: }; michael@0: michael@0: JS_FRIEND_API(const JSErrorFormatString *) michael@0: js_GetErrorMessage(void *userRef, const char *locale, const unsigned errorNumber) michael@0: { michael@0: if ((errorNumber > 0) && (errorNumber < JSErr_Limit)) michael@0: return &js_ErrorFormatString[errorNumber]; michael@0: return nullptr; michael@0: } michael@0: michael@0: bool michael@0: js::InvokeInterruptCallback(JSContext *cx) michael@0: { michael@0: JS_ASSERT_REQUEST_DEPTH(cx); michael@0: michael@0: JSRuntime *rt = cx->runtime(); michael@0: JS_ASSERT(rt->interrupt); michael@0: michael@0: // Reset the callback counter first, then run GC and yield. If another michael@0: // thread is racing us here we will accumulate another callback request michael@0: // which will be serviced at the next opportunity. michael@0: rt->interrupt = false; michael@0: michael@0: // IonMonkey sets its stack limit to UINTPTR_MAX to trigger interrupt michael@0: // callbacks. michael@0: rt->resetJitStackLimit(); michael@0: michael@0: js::gc::GCIfNeeded(cx); michael@0: michael@0: #ifdef JS_ION michael@0: #ifdef JS_THREADSAFE michael@0: rt->interruptPar = false; michael@0: #endif michael@0: michael@0: // A worker thread may have requested an interrupt after finishing an Ion michael@0: // compilation. michael@0: jit::AttachFinishedCompilations(cx); michael@0: #endif michael@0: michael@0: // Important: Additional callbacks can occur inside the callback handler michael@0: // if it re-enters the JS engine. The embedding must ensure that the michael@0: // callback is disconnected before attempting such re-entry. michael@0: JSInterruptCallback cb = cx->runtime()->interruptCallback; michael@0: if (!cb || cb(cx)) michael@0: return true; michael@0: michael@0: // No need to set aside any pending exception here: ComputeStackString michael@0: // already does that. michael@0: Rooted stack(cx, ComputeStackString(cx)); michael@0: const jschar *chars = stack ? stack->getCharsZ(cx) : nullptr; michael@0: if (!chars) michael@0: chars = MOZ_UTF16("(stack not available)"); michael@0: JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_WARNING, js_GetErrorMessage, nullptr, michael@0: JSMSG_TERMINATED, chars); michael@0: michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: js::HandleExecutionInterrupt(JSContext *cx) michael@0: { michael@0: if (cx->runtime()->interrupt) michael@0: return InvokeInterruptCallback(cx); michael@0: return true; michael@0: } michael@0: michael@0: ThreadSafeContext::ThreadSafeContext(JSRuntime *rt, PerThreadData *pt, ContextKind kind) michael@0: : ContextFriendFields(rt), michael@0: contextKind_(kind), michael@0: perThreadData(pt), michael@0: allocator_(nullptr) michael@0: { michael@0: } michael@0: michael@0: bool michael@0: ThreadSafeContext::isForkJoinContext() const michael@0: { michael@0: return contextKind_ == Context_ForkJoin; michael@0: } michael@0: michael@0: ForkJoinContext * michael@0: ThreadSafeContext::asForkJoinContext() michael@0: { michael@0: JS_ASSERT(isForkJoinContext()); michael@0: return reinterpret_cast(this); michael@0: } michael@0: michael@0: void michael@0: ThreadSafeContext::recoverFromOutOfMemory() michael@0: { michael@0: // If this is not a JSContext, there's nothing to do. michael@0: if (JSContext *maybecx = maybeJSContext()) { michael@0: if (maybecx->isExceptionPending()) { michael@0: MOZ_ASSERT(maybecx->isThrowingOutOfMemory()); michael@0: maybecx->clearPendingException(); michael@0: } else { michael@0: MOZ_ASSERT(maybecx->runtime()->hadOutOfMemory); michael@0: } michael@0: } michael@0: } michael@0: michael@0: JSContext::JSContext(JSRuntime *rt) michael@0: : ExclusiveContext(rt, &rt->mainThread, Context_JS), michael@0: throwing(false), michael@0: unwrappedException_(UndefinedValue()), michael@0: options_(), michael@0: reportGranularity(JS_DEFAULT_JITREPORT_GRANULARITY), michael@0: resolvingList(nullptr), michael@0: generatingError(false), michael@0: savedFrameChains_(), michael@0: defaultCompartmentObject_(nullptr), michael@0: cycleDetectorSet(MOZ_THIS_IN_INITIALIZER_LIST()), michael@0: errorReporter(nullptr), michael@0: data(nullptr), michael@0: data2(nullptr), michael@0: #ifdef JS_THREADSAFE michael@0: outstandingRequests(0), michael@0: #endif michael@0: iterValue(MagicValue(JS_NO_ITER_VALUE)), michael@0: jitIsBroken(false), michael@0: #ifdef MOZ_TRACE_JSCALLS michael@0: functionCallback(nullptr), michael@0: #endif michael@0: innermostGenerator_(nullptr) michael@0: { michael@0: #ifdef DEBUG michael@0: stackIterAssertionEnabled = true; michael@0: #endif michael@0: michael@0: JS_ASSERT(static_cast(this) == michael@0: ContextFriendFields::get(this)); michael@0: } michael@0: michael@0: JSContext::~JSContext() michael@0: { michael@0: /* Free the stuff hanging off of cx. */ michael@0: JS_ASSERT(!resolvingList); michael@0: } michael@0: michael@0: bool michael@0: JSContext::getPendingException(MutableHandleValue rval) michael@0: { michael@0: JS_ASSERT(throwing); michael@0: rval.set(unwrappedException_); michael@0: if (IsAtomsCompartment(compartment())) michael@0: return true; michael@0: clearPendingException(); michael@0: if (!compartment()->wrap(this, rval)) michael@0: return false; michael@0: assertSameCompartment(this, rval); michael@0: setPendingException(rval); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: JSContext::isThrowingOutOfMemory() michael@0: { michael@0: return throwing && unwrappedException_ == StringValue(names().outOfMemory); michael@0: } michael@0: michael@0: void michael@0: JSContext::enterGenerator(JSGenerator *gen) michael@0: { michael@0: JS_ASSERT(!gen->prevGenerator); michael@0: gen->prevGenerator = innermostGenerator_; michael@0: innermostGenerator_ = gen; michael@0: } michael@0: michael@0: void michael@0: JSContext::leaveGenerator(JSGenerator *gen) michael@0: { michael@0: JS_ASSERT(innermostGenerator_ == gen); michael@0: innermostGenerator_ = innermostGenerator_->prevGenerator; michael@0: gen->prevGenerator = nullptr; michael@0: } michael@0: michael@0: michael@0: bool michael@0: JSContext::runningWithTrustedPrincipals() const michael@0: { michael@0: return !compartment() || compartment()->principals == runtime()->trustedPrincipals(); michael@0: } michael@0: michael@0: bool michael@0: JSContext::saveFrameChain() michael@0: { michael@0: if (!savedFrameChains_.append(SavedFrameChain(compartment(), enterCompartmentDepth_))) michael@0: return false; michael@0: michael@0: if (Activation *act = mainThread().activation()) michael@0: act->saveFrameChain(); michael@0: michael@0: setCompartment(nullptr); michael@0: enterCompartmentDepth_ = 0; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: JSContext::restoreFrameChain() michael@0: { michael@0: JS_ASSERT(enterCompartmentDepth_ == 0); // We're about to clobber it, and it michael@0: // will be wrong forevermore. michael@0: SavedFrameChain sfc = savedFrameChains_.popCopy(); michael@0: setCompartment(sfc.compartment); michael@0: enterCompartmentDepth_ = sfc.enterCompartmentCount; michael@0: michael@0: if (Activation *act = mainThread().activation()) michael@0: act->restoreFrameChain(); michael@0: } michael@0: michael@0: bool michael@0: JSContext::currentlyRunning() const michael@0: { michael@0: for (ActivationIterator iter(runtime()); !iter.done(); ++iter) { michael@0: if (iter->cx() == this) { michael@0: if (iter->hasSavedFrameChain()) michael@0: return false; michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: static bool michael@0: ComputeIsJITBroken() michael@0: { michael@0: #if !defined(ANDROID) || defined(GONK) michael@0: return false; michael@0: #else // ANDROID michael@0: if (getenv("JS_IGNORE_JIT_BROKENNESS")) { michael@0: return false; michael@0: } michael@0: michael@0: std::string line; michael@0: michael@0: // Check for the known-bad kernel version (2.6.29). michael@0: std::ifstream osrelease("/proc/sys/kernel/osrelease"); michael@0: std::getline(osrelease, line); michael@0: __android_log_print(ANDROID_LOG_INFO, "Gecko", "Detected osrelease `%s'", michael@0: line.c_str()); michael@0: michael@0: if (line.npos == line.find("2.6.29")) { michael@0: // We're using something other than 2.6.29, so the JITs should work. michael@0: __android_log_print(ANDROID_LOG_INFO, "Gecko", "JITs are not broken"); michael@0: return false; michael@0: } michael@0: michael@0: // We're using 2.6.29, and this causes trouble with the JITs on i9000. michael@0: line = ""; michael@0: bool broken = false; michael@0: std::ifstream cpuinfo("/proc/cpuinfo"); michael@0: do { michael@0: if (0 == line.find("Hardware")) { michael@0: static const char* const blacklist[] = { michael@0: "SCH-I400", // Samsung Continuum michael@0: "SGH-T959", // Samsung i9000, Vibrant device michael@0: "SGH-I897", // Samsung i9000, Captivate device michael@0: "SCH-I500", // Samsung i9000, Fascinate device michael@0: "SPH-D700", // Samsung i9000, Epic device michael@0: "GT-I9000", // Samsung i9000, UK/Europe device michael@0: nullptr michael@0: }; michael@0: for (const char* const* hw = &blacklist[0]; *hw; ++hw) { michael@0: if (line.npos != line.find(*hw)) { michael@0: __android_log_print(ANDROID_LOG_INFO, "Gecko", michael@0: "Blacklisted device `%s'", *hw); michael@0: broken = true; michael@0: break; michael@0: } michael@0: } michael@0: break; michael@0: } michael@0: std::getline(cpuinfo, line); michael@0: } while(!cpuinfo.fail() && !cpuinfo.eof()); michael@0: michael@0: __android_log_print(ANDROID_LOG_INFO, "Gecko", "JITs are %sbroken", michael@0: broken ? "" : "not "); michael@0: michael@0: return broken; michael@0: #endif // ifndef ANDROID michael@0: } michael@0: michael@0: static bool michael@0: IsJITBrokenHere() michael@0: { michael@0: static bool computedIsBroken = false; michael@0: static bool isBroken = false; michael@0: if (!computedIsBroken) { michael@0: isBroken = ComputeIsJITBroken(); michael@0: computedIsBroken = true; michael@0: } michael@0: return isBroken; michael@0: } michael@0: michael@0: void michael@0: JSContext::updateJITEnabled() michael@0: { michael@0: jitIsBroken = IsJITBrokenHere(); michael@0: } michael@0: michael@0: size_t michael@0: JSContext::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const michael@0: { michael@0: /* michael@0: * There are other JSContext members that could be measured; the following michael@0: * ones have been found by DMD to be worth measuring. More stuff may be michael@0: * added later. michael@0: */ michael@0: return mallocSizeOf(this) + cycleDetectorSet.sizeOfExcludingThis(mallocSizeOf); michael@0: } michael@0: michael@0: void michael@0: JSContext::mark(JSTracer *trc) michael@0: { michael@0: /* Stack frames and slots are traced by StackSpace::mark. */ michael@0: michael@0: /* Mark other roots-by-definition in the JSContext. */ michael@0: if (defaultCompartmentObject_) michael@0: MarkObjectRoot(trc, &defaultCompartmentObject_, "default compartment object"); michael@0: if (isExceptionPending()) michael@0: MarkValueRoot(trc, &unwrappedException_, "unwrapped exception"); michael@0: michael@0: TraceCycleDetectionSet(trc, cycleDetectorSet); michael@0: michael@0: MarkValueRoot(trc, &iterValue, "iterValue"); michael@0: } michael@0: michael@0: void * michael@0: ThreadSafeContext::stackLimitAddressForJitCode(StackKind kind) michael@0: { michael@0: #ifdef JS_ARM_SIMULATOR michael@0: return runtime_->mainThread.addressOfSimulatorStackLimit(); michael@0: #endif michael@0: return stackLimitAddress(kind); michael@0: } michael@0: michael@0: JSVersion michael@0: JSContext::findVersion() const michael@0: { michael@0: if (JSScript *script = currentScript(nullptr, ALLOW_CROSS_COMPARTMENT)) michael@0: return script->getVersion(); michael@0: michael@0: if (compartment() && compartment()->options().version() != JSVERSION_UNKNOWN) michael@0: return compartment()->options().version(); michael@0: michael@0: return runtime()->defaultVersion(); michael@0: } michael@0: michael@0: #if defined JS_THREADSAFE && defined DEBUG michael@0: michael@0: JS::AutoCheckRequestDepth::AutoCheckRequestDepth(JSContext *cx) michael@0: : cx(cx) michael@0: { michael@0: JS_ASSERT(cx->runtime()->requestDepth || cx->runtime()->isHeapBusy()); michael@0: JS_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime())); michael@0: cx->runtime()->checkRequestDepth++; michael@0: } michael@0: michael@0: JS::AutoCheckRequestDepth::AutoCheckRequestDepth(ContextFriendFields *cxArg) michael@0: : cx(static_cast(cxArg)->maybeJSContext()) michael@0: { michael@0: if (cx) { michael@0: JS_ASSERT(cx->runtime()->requestDepth || cx->runtime()->isHeapBusy()); michael@0: JS_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime())); michael@0: cx->runtime()->checkRequestDepth++; michael@0: } michael@0: } michael@0: michael@0: JS::AutoCheckRequestDepth::~AutoCheckRequestDepth() michael@0: { michael@0: if (cx) { michael@0: JS_ASSERT(cx->runtime()->checkRequestDepth != 0); michael@0: cx->runtime()->checkRequestDepth--; michael@0: } michael@0: } michael@0: michael@0: #endif michael@0: michael@0: #ifdef JS_CRASH_DIAGNOSTICS michael@0: void CompartmentChecker::check(InterpreterFrame *fp) michael@0: { michael@0: if (fp) michael@0: check(fp->scopeChain()); michael@0: } michael@0: michael@0: void CompartmentChecker::check(AbstractFramePtr frame) michael@0: { michael@0: if (frame) michael@0: check(frame.scopeChain()); michael@0: } michael@0: #endif michael@0: michael@0: void michael@0: js::CrashAtUnhandlableOOM(const char *reason) michael@0: { michael@0: char msgbuf[1024]; michael@0: JS_snprintf(msgbuf, sizeof(msgbuf), "[unhandlable oom] %s", reason); michael@0: MOZ_ReportAssertionFailure(msgbuf, __FILE__, __LINE__); michael@0: MOZ_CRASH(); michael@0: }