js/src/jscntxt.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/src/jscntxt.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1394 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99:
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +/*
    1.11 + * JS execution context.
    1.12 + */
    1.13 +
    1.14 +#include "jscntxtinlines.h"
    1.15 +
    1.16 +#include "mozilla/ArrayUtils.h"
    1.17 +#include "mozilla/DebugOnly.h"
    1.18 +#include "mozilla/MemoryReporting.h"
    1.19 +
    1.20 +#include <ctype.h>
    1.21 +#include <stdarg.h>
    1.22 +#include <string.h>
    1.23 +#ifdef ANDROID
    1.24 +# include <android/log.h>
    1.25 +# include <fstream>
    1.26 +# include <string>
    1.27 +#endif  // ANDROID
    1.28 +
    1.29 +#include "jsatom.h"
    1.30 +#include "jscompartment.h"
    1.31 +#include "jsexn.h"
    1.32 +#include "jsfun.h"
    1.33 +#include "jsgc.h"
    1.34 +#include "jsiter.h"
    1.35 +#include "jsobj.h"
    1.36 +#include "jsopcode.h"
    1.37 +#include "jsprf.h"
    1.38 +#include "jspubtd.h"
    1.39 +#include "jsscript.h"
    1.40 +#include "jsstr.h"
    1.41 +#include "jstypes.h"
    1.42 +#include "jswatchpoint.h"
    1.43 +#include "jsworkers.h"
    1.44 +
    1.45 +#include "gc/Marking.h"
    1.46 +#ifdef JS_ION
    1.47 +#include "jit/Ion.h"
    1.48 +#endif
    1.49 +#include "js/CharacterEncoding.h"
    1.50 +#include "js/OldDebugAPI.h"
    1.51 +#include "vm/Shape.h"
    1.52 +#include "yarr/BumpPointerAllocator.h"
    1.53 +
    1.54 +#include "jsobjinlines.h"
    1.55 +#include "jsscriptinlines.h"
    1.56 +
    1.57 +#include "vm/Stack-inl.h"
    1.58 +
    1.59 +using namespace js;
    1.60 +using namespace js::gc;
    1.61 +
    1.62 +using mozilla::DebugOnly;
    1.63 +using mozilla::PodArrayZero;
    1.64 +using mozilla::PodZero;
    1.65 +using mozilla::PointerRangeSize;
    1.66 +
    1.67 +bool
    1.68 +js::AutoCycleDetector::init()
    1.69 +{
    1.70 +    ObjectSet &set = cx->cycleDetectorSet;
    1.71 +    hashsetAddPointer = set.lookupForAdd(obj);
    1.72 +    if (!hashsetAddPointer) {
    1.73 +        if (!set.add(hashsetAddPointer, obj))
    1.74 +            return false;
    1.75 +        cyclic = false;
    1.76 +        hashsetGenerationAtInit = set.generation();
    1.77 +    }
    1.78 +    return true;
    1.79 +}
    1.80 +
    1.81 +js::AutoCycleDetector::~AutoCycleDetector()
    1.82 +{
    1.83 +    if (!cyclic) {
    1.84 +        if (hashsetGenerationAtInit == cx->cycleDetectorSet.generation())
    1.85 +            cx->cycleDetectorSet.remove(hashsetAddPointer);
    1.86 +        else
    1.87 +            cx->cycleDetectorSet.remove(obj);
    1.88 +    }
    1.89 +}
    1.90 +
    1.91 +void
    1.92 +js::TraceCycleDetectionSet(JSTracer *trc, js::ObjectSet &set)
    1.93 +{
    1.94 +    for (js::ObjectSet::Enum e(set); !e.empty(); e.popFront()) {
    1.95 +        JSObject *prior = e.front();
    1.96 +        MarkObjectRoot(trc, const_cast<JSObject **>(&e.front()), "cycle detector table entry");
    1.97 +        if (prior != e.front())
    1.98 +            e.rekeyFront(e.front());
    1.99 +    }
   1.100 +}
   1.101 +
   1.102 +void
   1.103 +JSCompartment::sweepCallsiteClones()
   1.104 +{
   1.105 +    if (callsiteClones.initialized()) {
   1.106 +        for (CallsiteCloneTable::Enum e(callsiteClones); !e.empty(); e.popFront()) {
   1.107 +            CallsiteCloneKey key = e.front().key();
   1.108 +            JSFunction *fun = e.front().value();
   1.109 +            if (!IsScriptMarked(&key.script) || !IsObjectMarked(&fun))
   1.110 +                e.removeFront();
   1.111 +        }
   1.112 +    }
   1.113 +}
   1.114 +
   1.115 +JSFunction *
   1.116 +js::ExistingCloneFunctionAtCallsite(const CallsiteCloneTable &table, JSFunction *fun,
   1.117 +                                    JSScript *script, jsbytecode *pc)
   1.118 +{
   1.119 +    JS_ASSERT(fun->nonLazyScript()->shouldCloneAtCallsite());
   1.120 +    JS_ASSERT(!fun->nonLazyScript()->enclosingStaticScope());
   1.121 +    JS_ASSERT(types::UseNewTypeForClone(fun));
   1.122 +
   1.123 +    /*
   1.124 +     * If we start allocating function objects in the nursery, then the callsite
   1.125 +     * clone table will need a postbarrier.
   1.126 +     */
   1.127 +    JS_ASSERT(fun->isTenured());
   1.128 +
   1.129 +    if (!table.initialized())
   1.130 +        return nullptr;
   1.131 +
   1.132 +    CallsiteCloneTable::Ptr p = table.readonlyThreadsafeLookup(CallsiteCloneKey(fun, script, script->pcToOffset(pc)));
   1.133 +    if (p)
   1.134 +        return p->value();
   1.135 +
   1.136 +    return nullptr;
   1.137 +}
   1.138 +
   1.139 +JSFunction *
   1.140 +js::CloneFunctionAtCallsite(JSContext *cx, HandleFunction fun, HandleScript script, jsbytecode *pc)
   1.141 +{
   1.142 +    if (JSFunction *clone = ExistingCloneFunctionAtCallsite(cx->compartment()->callsiteClones, fun, script, pc))
   1.143 +        return clone;
   1.144 +
   1.145 +    RootedObject parent(cx, fun->environment());
   1.146 +    JSFunction *clone = CloneFunctionObject(cx, fun, parent);
   1.147 +    if (!clone)
   1.148 +        return nullptr;
   1.149 +
   1.150 +    /*
   1.151 +     * Store a link back to the original for function.caller and avoid cloning
   1.152 +     * clones.
   1.153 +     */
   1.154 +    clone->nonLazyScript()->setIsCallsiteClone(fun);
   1.155 +
   1.156 +    typedef CallsiteCloneKey Key;
   1.157 +    typedef CallsiteCloneTable Table;
   1.158 +
   1.159 +    Table &table = cx->compartment()->callsiteClones;
   1.160 +    if (!table.initialized() && !table.init())
   1.161 +        return nullptr;
   1.162 +
   1.163 +    if (!table.putNew(Key(fun, script, script->pcToOffset(pc)), clone))
   1.164 +        return nullptr;
   1.165 +
   1.166 +    return clone;
   1.167 +}
   1.168 +
   1.169 +JSContext *
   1.170 +js::NewContext(JSRuntime *rt, size_t stackChunkSize)
   1.171 +{
   1.172 +    JS_AbortIfWrongThread(rt);
   1.173 +
   1.174 +    JSContext *cx = js_new<JSContext>(rt);
   1.175 +    if (!cx)
   1.176 +        return nullptr;
   1.177 +
   1.178 +    if (!cx->cycleDetectorSet.init()) {
   1.179 +        js_delete(cx);
   1.180 +        return nullptr;
   1.181 +    }
   1.182 +
   1.183 +    /*
   1.184 +     * Here the GC lock is still held after js_InitContextThreadAndLockGC took it and
   1.185 +     * the GC is not running on another thread.
   1.186 +     */
   1.187 +    rt->contextList.insertBack(cx);
   1.188 +
   1.189 +    /*
   1.190 +     * If cx is the first context on this runtime, initialize well-known atoms,
   1.191 +     * keywords, numbers, strings and self-hosted scripts. If one of these
   1.192 +     * steps should fail, the runtime will be left in a partially initialized
   1.193 +     * state, with zeroes and nulls stored in the default-initialized remainder
   1.194 +     * of the struct.
   1.195 +     */
   1.196 +    if (!rt->haveCreatedContext) {
   1.197 +#ifdef JS_THREADSAFE
   1.198 +        JS_BeginRequest(cx);
   1.199 +#endif
   1.200 +        bool ok = rt->initializeAtoms(cx);
   1.201 +        if (ok)
   1.202 +            ok = rt->initSelfHosting(cx);
   1.203 +
   1.204 +        if (ok && !rt->parentRuntime)
   1.205 +            ok = rt->transformToPermanentAtoms();
   1.206 +
   1.207 +#ifdef JS_THREADSAFE
   1.208 +        JS_EndRequest(cx);
   1.209 +#endif
   1.210 +        if (!ok) {
   1.211 +            DestroyContext(cx, DCM_NEW_FAILED);
   1.212 +            return nullptr;
   1.213 +        }
   1.214 +
   1.215 +        rt->haveCreatedContext = true;
   1.216 +    }
   1.217 +
   1.218 +    JSContextCallback cxCallback = rt->cxCallback;
   1.219 +    if (cxCallback && !cxCallback(cx, JSCONTEXT_NEW, rt->cxCallbackData)) {
   1.220 +        DestroyContext(cx, DCM_NEW_FAILED);
   1.221 +        return nullptr;
   1.222 +    }
   1.223 +
   1.224 +    return cx;
   1.225 +}
   1.226 +
   1.227 +void
   1.228 +js::DestroyContext(JSContext *cx, DestroyContextMode mode)
   1.229 +{
   1.230 +    JSRuntime *rt = cx->runtime();
   1.231 +    JS_AbortIfWrongThread(rt);
   1.232 +
   1.233 +#ifdef JS_THREADSAFE
   1.234 +    if (cx->outstandingRequests != 0)
   1.235 +        MOZ_CRASH();
   1.236 +#endif
   1.237 +
   1.238 +#if defined(JSGC_USE_EXACT_ROOTING) && defined(DEBUG)
   1.239 +    for (int i = 0; i < THING_ROOT_LIMIT; ++i)
   1.240 +        JS_ASSERT(cx->thingGCRooters[i] == nullptr);
   1.241 +#endif
   1.242 +
   1.243 +    if (mode != DCM_NEW_FAILED) {
   1.244 +        if (JSContextCallback cxCallback = rt->cxCallback) {
   1.245 +            /*
   1.246 +             * JSCONTEXT_DESTROY callback is not allowed to fail and must
   1.247 +             * return true.
   1.248 +             */
   1.249 +            JS_ALWAYS_TRUE(cxCallback(cx, JSCONTEXT_DESTROY,
   1.250 +                                      rt->cxCallbackData));
   1.251 +        }
   1.252 +    }
   1.253 +
   1.254 +    cx->remove();
   1.255 +    bool last = !rt->hasContexts();
   1.256 +    if (last) {
   1.257 +        /*
   1.258 +         * Dump remaining type inference results while we still have a context.
   1.259 +         * This printing depends on atoms still existing.
   1.260 +         */
   1.261 +        for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next())
   1.262 +            c->types.print(cx, false);
   1.263 +    }
   1.264 +    if (mode == DCM_FORCE_GC) {
   1.265 +        JS_ASSERT(!rt->isHeapBusy());
   1.266 +        JS::PrepareForFullGC(rt);
   1.267 +        GC(rt, GC_NORMAL, JS::gcreason::DESTROY_CONTEXT);
   1.268 +    }
   1.269 +    js_delete_poison(cx);
   1.270 +}
   1.271 +
   1.272 +bool
   1.273 +AutoResolving::alreadyStartedSlow() const
   1.274 +{
   1.275 +    JS_ASSERT(link);
   1.276 +    AutoResolving *cursor = link;
   1.277 +    do {
   1.278 +        JS_ASSERT(this != cursor);
   1.279 +        if (object.get() == cursor->object && id.get() == cursor->id && kind == cursor->kind)
   1.280 +            return true;
   1.281 +    } while (!!(cursor = cursor->link));
   1.282 +    return false;
   1.283 +}
   1.284 +
   1.285 +static void
   1.286 +ReportError(JSContext *cx, const char *message, JSErrorReport *reportp,
   1.287 +            JSErrorCallback callback, void *userRef)
   1.288 +{
   1.289 +    /*
   1.290 +     * Check the error report, and set a JavaScript-catchable exception
   1.291 +     * if the error is defined to have an associated exception.  If an
   1.292 +     * exception is thrown, then the JSREPORT_EXCEPTION flag will be set
   1.293 +     * on the error report, and exception-aware hosts should ignore it.
   1.294 +     */
   1.295 +    JS_ASSERT(reportp);
   1.296 +    if ((!callback || callback == js_GetErrorMessage) &&
   1.297 +        reportp->errorNumber == JSMSG_UNCAUGHT_EXCEPTION)
   1.298 +    {
   1.299 +        reportp->flags |= JSREPORT_EXCEPTION;
   1.300 +    }
   1.301 +
   1.302 +    /*
   1.303 +     * Call the error reporter only if an exception wasn't raised.
   1.304 +     *
   1.305 +     * If an exception was raised, then we call the debugErrorHook
   1.306 +     * (if present) to give it a chance to see the error before it
   1.307 +     * propagates out of scope.  This is needed for compatibility
   1.308 +     * with the old scheme.
   1.309 +     */
   1.310 +    if (!JS_IsRunning(cx) || !js_ErrorToException(cx, message, reportp, callback, userRef)) {
   1.311 +        if (message)
   1.312 +            CallErrorReporter(cx, message, reportp);
   1.313 +    } else if (JSDebugErrorHook hook = cx->runtime()->debugHooks.debugErrorHook) {
   1.314 +        /*
   1.315 +         * If we've already chewed up all the C stack, don't call into the
   1.316 +         * error reporter since this may trigger an infinite recursion where
   1.317 +         * the reporter triggers an over-recursion.
   1.318 +         */
   1.319 +        int stackDummy;
   1.320 +        if (!JS_CHECK_STACK_SIZE(GetNativeStackLimit(cx), &stackDummy))
   1.321 +            return;
   1.322 +
   1.323 +        if (cx->errorReporter)
   1.324 +            hook(cx, message, reportp, cx->runtime()->debugHooks.debugErrorHookData);
   1.325 +    }
   1.326 +}
   1.327 +
   1.328 +/*
   1.329 + * The given JSErrorReport object have been zeroed and must not outlive
   1.330 + * cx->fp() (otherwise report->originPrincipals may become invalid).
   1.331 + */
   1.332 +static void
   1.333 +PopulateReportBlame(JSContext *cx, JSErrorReport *report)
   1.334 +{
   1.335 +    /*
   1.336 +     * Walk stack until we find a frame that is associated with a non-builtin
   1.337 +     * rather than a builtin frame.
   1.338 +     */
   1.339 +    NonBuiltinFrameIter iter(cx);
   1.340 +    if (iter.done())
   1.341 +        return;
   1.342 +
   1.343 +    report->filename = iter.scriptFilename();
   1.344 +    report->lineno = iter.computeLine(&report->column);
   1.345 +    report->originPrincipals = iter.originPrincipals();
   1.346 +}
   1.347 +
   1.348 +/*
   1.349 + * Since memory has been exhausted, avoid the normal error-handling path which
   1.350 + * allocates an error object, report and callstack. If code is running, simply
   1.351 + * throw the static atom "out of memory". If code is not running, call the
   1.352 + * error reporter directly.
   1.353 + *
   1.354 + * Furthermore, callers of js_ReportOutOfMemory (viz., malloc) assume a GC does
   1.355 + * not occur, so GC must be avoided or suppressed.
   1.356 + */
   1.357 +void
   1.358 +js_ReportOutOfMemory(ThreadSafeContext *cxArg)
   1.359 +{
   1.360 +#ifdef JS_MORE_DETERMINISTIC
   1.361 +    /*
   1.362 +     * OOMs are non-deterministic, especially across different execution modes
   1.363 +     * (e.g. interpreter vs JIT). In more-deterministic builds, print to stderr
   1.364 +     * so that the fuzzers can detect this.
   1.365 +     */
   1.366 +    fprintf(stderr, "js_ReportOutOfMemory called\n");
   1.367 +#endif
   1.368 +
   1.369 +    if (cxArg->isForkJoinContext()) {
   1.370 +        cxArg->asForkJoinContext()->setPendingAbortFatal(ParallelBailoutOutOfMemory);
   1.371 +        return;
   1.372 +    }
   1.373 +
   1.374 +    if (!cxArg->isJSContext())
   1.375 +        return;
   1.376 +
   1.377 +    JSContext *cx = cxArg->asJSContext();
   1.378 +    cx->runtime()->hadOutOfMemory = true;
   1.379 +
   1.380 +    /* Report the oom. */
   1.381 +    if (JS::OutOfMemoryCallback oomCallback = cx->runtime()->oomCallback) {
   1.382 +        AutoSuppressGC suppressGC(cx);
   1.383 +        oomCallback(cx);
   1.384 +    }
   1.385 +
   1.386 +    if (JS_IsRunning(cx)) {
   1.387 +        cx->setPendingException(StringValue(cx->names().outOfMemory));
   1.388 +        return;
   1.389 +    }
   1.390 +
   1.391 +    /* Get the message for this error, but we don't expand any arguments. */
   1.392 +    const JSErrorFormatString *efs =
   1.393 +        js_GetLocalizedErrorMessage(cx, nullptr, nullptr, JSMSG_OUT_OF_MEMORY);
   1.394 +    const char *msg = efs ? efs->format : "Out of memory";
   1.395 +
   1.396 +    /* Fill out the report, but don't do anything that requires allocation. */
   1.397 +    JSErrorReport report;
   1.398 +    PodZero(&report);
   1.399 +    report.flags = JSREPORT_ERROR;
   1.400 +    report.errorNumber = JSMSG_OUT_OF_MEMORY;
   1.401 +    PopulateReportBlame(cx, &report);
   1.402 +
   1.403 +    /* Report the error. */
   1.404 +    if (JSErrorReporter onError = cx->errorReporter) {
   1.405 +        AutoSuppressGC suppressGC(cx);
   1.406 +        onError(cx, msg, &report);
   1.407 +    }
   1.408 +
   1.409 +    /*
   1.410 +     * We would like to enforce the invariant that any exception reported
   1.411 +     * during an OOM situation does not require wrapping. Besides avoiding
   1.412 +     * allocation when memory is low, this reduces the number of places where
   1.413 +     * we might need to GC.
   1.414 +     *
   1.415 +     * When JS code is running, we set the pending exception to an atom, which
   1.416 +     * does not need wrapping. If no JS code is running, no exception should be
   1.417 +     * set at all.
   1.418 +     */
   1.419 +    JS_ASSERT(!cx->isExceptionPending());
   1.420 +}
   1.421 +
   1.422 +JS_FRIEND_API(void)
   1.423 +js_ReportOverRecursed(JSContext *maybecx)
   1.424 +{
   1.425 +#ifdef JS_MORE_DETERMINISTIC
   1.426 +    /*
   1.427 +     * We cannot make stack depth deterministic across different
   1.428 +     * implementations (e.g. JIT vs. interpreter will differ in
   1.429 +     * their maximum stack depth).
   1.430 +     * However, we can detect externally when we hit the maximum
   1.431 +     * stack depth which is useful for external testing programs
   1.432 +     * like fuzzers.
   1.433 +     */
   1.434 +    fprintf(stderr, "js_ReportOverRecursed called\n");
   1.435 +#endif
   1.436 +    if (maybecx)
   1.437 +        JS_ReportErrorNumber(maybecx, js_GetErrorMessage, nullptr, JSMSG_OVER_RECURSED);
   1.438 +}
   1.439 +
   1.440 +void
   1.441 +js_ReportOverRecursed(ThreadSafeContext *cx)
   1.442 +{
   1.443 +    if (cx->isJSContext())
   1.444 +        js_ReportOverRecursed(cx->asJSContext());
   1.445 +    else if (cx->isExclusiveContext())
   1.446 +        cx->asExclusiveContext()->addPendingOverRecursed();
   1.447 +}
   1.448 +
   1.449 +void
   1.450 +js_ReportAllocationOverflow(ThreadSafeContext *cxArg)
   1.451 +{
   1.452 +    if (!cxArg)
   1.453 +        return;
   1.454 +
   1.455 +    if (cxArg->isForkJoinContext()) {
   1.456 +        cxArg->asForkJoinContext()->setPendingAbortFatal(ParallelBailoutOutOfMemory);
   1.457 +        return;
   1.458 +    }
   1.459 +
   1.460 +    if (!cxArg->isJSContext())
   1.461 +        return;
   1.462 +    JSContext *cx = cxArg->asJSContext();
   1.463 +
   1.464 +    AutoSuppressGC suppressGC(cx);
   1.465 +    JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_ALLOC_OVERFLOW);
   1.466 +}
   1.467 +
   1.468 +/*
   1.469 + * Given flags and the state of cx, decide whether we should report an
   1.470 + * error, a warning, or just continue execution normally.  Return
   1.471 + * true if we should continue normally, without reporting anything;
   1.472 + * otherwise, adjust *flags as appropriate and return false.
   1.473 + */
   1.474 +static bool
   1.475 +checkReportFlags(JSContext *cx, unsigned *flags)
   1.476 +{
   1.477 +    if (JSREPORT_IS_STRICT_MODE_ERROR(*flags)) {
   1.478 +        /*
   1.479 +         * Error in strict code; warning with extra warnings option; okay
   1.480 +         * otherwise.  We assume that if the top frame is a native, then it is
   1.481 +         * strict if the nearest scripted frame is strict, see bug 536306.
   1.482 +         */
   1.483 +        JSScript *script = cx->currentScript();
   1.484 +        if (script && script->strict())
   1.485 +            *flags &= ~JSREPORT_WARNING;
   1.486 +        else if (cx->options().extraWarnings())
   1.487 +            *flags |= JSREPORT_WARNING;
   1.488 +        else
   1.489 +            return true;
   1.490 +    } else if (JSREPORT_IS_STRICT(*flags)) {
   1.491 +        /* Warning/error only when JSOPTION_STRICT is set. */
   1.492 +        if (!cx->options().extraWarnings())
   1.493 +            return true;
   1.494 +    }
   1.495 +
   1.496 +    /* Warnings become errors when JSOPTION_WERROR is set. */
   1.497 +    if (JSREPORT_IS_WARNING(*flags) && cx->options().werror())
   1.498 +        *flags &= ~JSREPORT_WARNING;
   1.499 +
   1.500 +    return false;
   1.501 +}
   1.502 +
   1.503 +bool
   1.504 +js_ReportErrorVA(JSContext *cx, unsigned flags, const char *format, va_list ap)
   1.505 +{
   1.506 +    char *message;
   1.507 +    jschar *ucmessage;
   1.508 +    size_t messagelen;
   1.509 +    JSErrorReport report;
   1.510 +    bool warning;
   1.511 +
   1.512 +    if (checkReportFlags(cx, &flags))
   1.513 +        return true;
   1.514 +
   1.515 +    message = JS_vsmprintf(format, ap);
   1.516 +    if (!message)
   1.517 +        return false;
   1.518 +    messagelen = strlen(message);
   1.519 +
   1.520 +    PodZero(&report);
   1.521 +    report.flags = flags;
   1.522 +    report.errorNumber = JSMSG_USER_DEFINED_ERROR;
   1.523 +    report.ucmessage = ucmessage = InflateString(cx, message, &messagelen);
   1.524 +    PopulateReportBlame(cx, &report);
   1.525 +
   1.526 +    warning = JSREPORT_IS_WARNING(report.flags);
   1.527 +
   1.528 +    ReportError(cx, message, &report, nullptr, nullptr);
   1.529 +    js_free(message);
   1.530 +    js_free(ucmessage);
   1.531 +    return warning;
   1.532 +}
   1.533 +
   1.534 +/* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */
   1.535 +void
   1.536 +js::ReportUsageError(JSContext *cx, HandleObject callee, const char *msg)
   1.537 +{
   1.538 +    const char *usageStr = "usage";
   1.539 +    PropertyName *usageAtom = Atomize(cx, usageStr, strlen(usageStr))->asPropertyName();
   1.540 +    RootedId id(cx, NameToId(usageAtom));
   1.541 +    DebugOnly<Shape *> shape = static_cast<Shape *>(callee->nativeLookup(cx, id));
   1.542 +    JS_ASSERT(!shape->configurable());
   1.543 +    JS_ASSERT(!shape->writable());
   1.544 +    JS_ASSERT(shape->hasDefaultGetter());
   1.545 +
   1.546 +    RootedValue usage(cx);
   1.547 +    if (!JS_LookupProperty(cx, callee, "usage", &usage))
   1.548 +        return;
   1.549 +
   1.550 +    if (JSVAL_IS_VOID(usage)) {
   1.551 +        JS_ReportError(cx, "%s", msg);
   1.552 +    } else {
   1.553 +        JSString *str = JSVAL_TO_STRING(usage);
   1.554 +        JS::Anchor<JSString *> a_str(str);
   1.555 +        const jschar *chars = JS_GetStringCharsZ(cx, str);
   1.556 +        if (!chars)
   1.557 +            return;
   1.558 +        JS_ReportError(cx, "%s. Usage: %hs", msg, chars);
   1.559 +    }
   1.560 +}
   1.561 +
   1.562 +bool
   1.563 +js::PrintError(JSContext *cx, FILE *file, const char *message, JSErrorReport *report,
   1.564 +               bool reportWarnings)
   1.565 +{
   1.566 +    if (!report) {
   1.567 +        fprintf(file, "%s\n", message);
   1.568 +        fflush(file);
   1.569 +        return false;
   1.570 +    }
   1.571 +
   1.572 +    /* Conditionally ignore reported warnings. */
   1.573 +    if (JSREPORT_IS_WARNING(report->flags) && !reportWarnings)
   1.574 +        return false;
   1.575 +
   1.576 +    char *prefix = nullptr;
   1.577 +    if (report->filename)
   1.578 +        prefix = JS_smprintf("%s:", report->filename);
   1.579 +    if (report->lineno) {
   1.580 +        char *tmp = prefix;
   1.581 +        prefix = JS_smprintf("%s%u:%u ", tmp ? tmp : "", report->lineno, report->column);
   1.582 +        JS_free(cx, tmp);
   1.583 +    }
   1.584 +    if (JSREPORT_IS_WARNING(report->flags)) {
   1.585 +        char *tmp = prefix;
   1.586 +        prefix = JS_smprintf("%s%swarning: ",
   1.587 +                             tmp ? tmp : "",
   1.588 +                             JSREPORT_IS_STRICT(report->flags) ? "strict " : "");
   1.589 +        JS_free(cx, tmp);
   1.590 +    }
   1.591 +
   1.592 +    /* embedded newlines -- argh! */
   1.593 +    const char *ctmp;
   1.594 +    while ((ctmp = strchr(message, '\n')) != 0) {
   1.595 +        ctmp++;
   1.596 +        if (prefix)
   1.597 +            fputs(prefix, file);
   1.598 +        fwrite(message, 1, ctmp - message, file);
   1.599 +        message = ctmp;
   1.600 +    }
   1.601 +
   1.602 +    /* If there were no filename or lineno, the prefix might be empty */
   1.603 +    if (prefix)
   1.604 +        fputs(prefix, file);
   1.605 +    fputs(message, file);
   1.606 +
   1.607 +    if (report->linebuf) {
   1.608 +        /* report->linebuf usually ends with a newline. */
   1.609 +        int n = strlen(report->linebuf);
   1.610 +        fprintf(file, ":\n%s%s%s%s",
   1.611 +                prefix,
   1.612 +                report->linebuf,
   1.613 +                (n > 0 && report->linebuf[n-1] == '\n') ? "" : "\n",
   1.614 +                prefix);
   1.615 +        n = report->tokenptr - report->linebuf;
   1.616 +        for (int i = 0, j = 0; i < n; i++) {
   1.617 +            if (report->linebuf[i] == '\t') {
   1.618 +                for (int k = (j + 8) & ~7; j < k; j++) {
   1.619 +                    fputc('.', file);
   1.620 +                }
   1.621 +                continue;
   1.622 +            }
   1.623 +            fputc('.', file);
   1.624 +            j++;
   1.625 +        }
   1.626 +        fputc('^', file);
   1.627 +    }
   1.628 +    fputc('\n', file);
   1.629 +    fflush(file);
   1.630 +    JS_free(cx, prefix);
   1.631 +    return true;
   1.632 +}
   1.633 +
   1.634 +char *
   1.635 +js_strdup(ExclusiveContext *cx, const char *s)
   1.636 +{
   1.637 +    size_t n = strlen(s) + 1;
   1.638 +    void *p = cx->malloc_(n);
   1.639 +    if (!p)
   1.640 +        return nullptr;
   1.641 +    return (char *)js_memcpy(p, s, n);
   1.642 +}
   1.643 +
   1.644 +/*
   1.645 + * The arguments from ap need to be packaged up into an array and stored
   1.646 + * into the report struct.
   1.647 + *
   1.648 + * The format string addressed by the error number may contain operands
   1.649 + * identified by the format {N}, where N is a decimal digit. Each of these
   1.650 + * is to be replaced by the Nth argument from the va_list. The complete
   1.651 + * message is placed into reportp->ucmessage converted to a JSString.
   1.652 + *
   1.653 + * Returns true if the expansion succeeds (can fail if out of memory).
   1.654 + */
   1.655 +bool
   1.656 +js_ExpandErrorArguments(ExclusiveContext *cx, JSErrorCallback callback,
   1.657 +                        void *userRef, const unsigned errorNumber,
   1.658 +                        char **messagep, JSErrorReport *reportp,
   1.659 +                        ErrorArgumentsType argumentsType, va_list ap)
   1.660 +{
   1.661 +    const JSErrorFormatString *efs;
   1.662 +    int i;
   1.663 +    int argCount;
   1.664 +    bool messageArgsPassed = !!reportp->messageArgs;
   1.665 +
   1.666 +    *messagep = nullptr;
   1.667 +
   1.668 +    /* Most calls supply js_GetErrorMessage; if this is so, assume nullptr. */
   1.669 +    if (!callback || callback == js_GetErrorMessage)
   1.670 +        efs = js_GetLocalizedErrorMessage(cx, userRef, nullptr, errorNumber);
   1.671 +    else
   1.672 +        efs = callback(userRef, nullptr, errorNumber);
   1.673 +    if (efs) {
   1.674 +        reportp->exnType = efs->exnType;
   1.675 +
   1.676 +        size_t totalArgsLength = 0;
   1.677 +        size_t argLengths[10]; /* only {0} thru {9} supported */
   1.678 +        argCount = efs->argCount;
   1.679 +        JS_ASSERT(argCount <= 10);
   1.680 +        if (argCount > 0) {
   1.681 +            /*
   1.682 +             * Gather the arguments into an array, and accumulate
   1.683 +             * their sizes. We allocate 1 more than necessary and
   1.684 +             * null it out to act as the caboose when we free the
   1.685 +             * pointers later.
   1.686 +             */
   1.687 +            if (messageArgsPassed) {
   1.688 +                JS_ASSERT(!reportp->messageArgs[argCount]);
   1.689 +            } else {
   1.690 +                reportp->messageArgs = cx->pod_malloc<const jschar*>(argCount + 1);
   1.691 +                if (!reportp->messageArgs)
   1.692 +                    return false;
   1.693 +                /* nullptr-terminate for easy copying. */
   1.694 +                reportp->messageArgs[argCount] = nullptr;
   1.695 +            }
   1.696 +            for (i = 0; i < argCount; i++) {
   1.697 +                if (messageArgsPassed) {
   1.698 +                    /* Do nothing. */
   1.699 +                } else if (argumentsType == ArgumentsAreASCII) {
   1.700 +                    char *charArg = va_arg(ap, char *);
   1.701 +                    size_t charArgLength = strlen(charArg);
   1.702 +                    reportp->messageArgs[i] = InflateString(cx, charArg, &charArgLength);
   1.703 +                    if (!reportp->messageArgs[i])
   1.704 +                        goto error;
   1.705 +                } else {
   1.706 +                    reportp->messageArgs[i] = va_arg(ap, jschar *);
   1.707 +                }
   1.708 +                argLengths[i] = js_strlen(reportp->messageArgs[i]);
   1.709 +                totalArgsLength += argLengths[i];
   1.710 +            }
   1.711 +        }
   1.712 +        /*
   1.713 +         * Parse the error format, substituting the argument X
   1.714 +         * for {X} in the format.
   1.715 +         */
   1.716 +        if (argCount > 0) {
   1.717 +            if (efs->format) {
   1.718 +                jschar *buffer, *fmt, *out;
   1.719 +                int expandedArgs = 0;
   1.720 +                size_t expandedLength;
   1.721 +                size_t len = strlen(efs->format);
   1.722 +
   1.723 +                buffer = fmt = InflateString(cx, efs->format, &len);
   1.724 +                if (!buffer)
   1.725 +                    goto error;
   1.726 +                expandedLength = len
   1.727 +                                 - (3 * argCount)       /* exclude the {n} */
   1.728 +                                 + totalArgsLength;
   1.729 +
   1.730 +                /*
   1.731 +                * Note - the above calculation assumes that each argument
   1.732 +                * is used once and only once in the expansion !!!
   1.733 +                */
   1.734 +                reportp->ucmessage = out = cx->pod_malloc<jschar>(expandedLength + 1);
   1.735 +                if (!out) {
   1.736 +                    js_free(buffer);
   1.737 +                    goto error;
   1.738 +                }
   1.739 +                while (*fmt) {
   1.740 +                    if (*fmt == '{') {
   1.741 +                        if (isdigit(fmt[1])) {
   1.742 +                            int d = JS7_UNDEC(fmt[1]);
   1.743 +                            JS_ASSERT(d < argCount);
   1.744 +                            js_strncpy(out, reportp->messageArgs[d],
   1.745 +                                       argLengths[d]);
   1.746 +                            out += argLengths[d];
   1.747 +                            fmt += 3;
   1.748 +                            expandedArgs++;
   1.749 +                            continue;
   1.750 +                        }
   1.751 +                    }
   1.752 +                    *out++ = *fmt++;
   1.753 +                }
   1.754 +                JS_ASSERT(expandedArgs == argCount);
   1.755 +                *out = 0;
   1.756 +                js_free(buffer);
   1.757 +                TwoByteChars ucmsg(reportp->ucmessage,
   1.758 +                                   PointerRangeSize(static_cast<const jschar *>(reportp->ucmessage),
   1.759 +                                                    static_cast<const jschar *>(out)));
   1.760 +                *messagep = LossyTwoByteCharsToNewLatin1CharsZ(cx, ucmsg).c_str();
   1.761 +                if (!*messagep)
   1.762 +                    goto error;
   1.763 +            }
   1.764 +        } else {
   1.765 +            /* Non-null messageArgs should have at least one non-null arg. */
   1.766 +            JS_ASSERT(!reportp->messageArgs);
   1.767 +            /*
   1.768 +             * Zero arguments: the format string (if it exists) is the
   1.769 +             * entire message.
   1.770 +             */
   1.771 +            if (efs->format) {
   1.772 +                size_t len;
   1.773 +                *messagep = js_strdup(cx, efs->format);
   1.774 +                if (!*messagep)
   1.775 +                    goto error;
   1.776 +                len = strlen(*messagep);
   1.777 +                reportp->ucmessage = InflateString(cx, *messagep, &len);
   1.778 +                if (!reportp->ucmessage)
   1.779 +                    goto error;
   1.780 +            }
   1.781 +        }
   1.782 +    }
   1.783 +    if (*messagep == nullptr) {
   1.784 +        /* where's the right place for this ??? */
   1.785 +        const char *defaultErrorMessage
   1.786 +            = "No error message available for error number %d";
   1.787 +        size_t nbytes = strlen(defaultErrorMessage) + 16;
   1.788 +        *messagep = cx->pod_malloc<char>(nbytes);
   1.789 +        if (!*messagep)
   1.790 +            goto error;
   1.791 +        JS_snprintf(*messagep, nbytes, defaultErrorMessage, errorNumber);
   1.792 +    }
   1.793 +    return true;
   1.794 +
   1.795 +error:
   1.796 +    if (!messageArgsPassed && reportp->messageArgs) {
   1.797 +        /* free the arguments only if we allocated them */
   1.798 +        if (argumentsType == ArgumentsAreASCII) {
   1.799 +            i = 0;
   1.800 +            while (reportp->messageArgs[i])
   1.801 +                js_free((void *)reportp->messageArgs[i++]);
   1.802 +        }
   1.803 +        js_free((void *)reportp->messageArgs);
   1.804 +        reportp->messageArgs = nullptr;
   1.805 +    }
   1.806 +    if (reportp->ucmessage) {
   1.807 +        js_free((void *)reportp->ucmessage);
   1.808 +        reportp->ucmessage = nullptr;
   1.809 +    }
   1.810 +    if (*messagep) {
   1.811 +        js_free((void *)*messagep);
   1.812 +        *messagep = nullptr;
   1.813 +    }
   1.814 +    return false;
   1.815 +}
   1.816 +
   1.817 +bool
   1.818 +js_ReportErrorNumberVA(JSContext *cx, unsigned flags, JSErrorCallback callback,
   1.819 +                       void *userRef, const unsigned errorNumber,
   1.820 +                       ErrorArgumentsType argumentsType, va_list ap)
   1.821 +{
   1.822 +    JSErrorReport report;
   1.823 +    char *message;
   1.824 +    bool warning;
   1.825 +
   1.826 +    if (checkReportFlags(cx, &flags))
   1.827 +        return true;
   1.828 +    warning = JSREPORT_IS_WARNING(flags);
   1.829 +
   1.830 +    PodZero(&report);
   1.831 +    report.flags = flags;
   1.832 +    report.errorNumber = errorNumber;
   1.833 +    PopulateReportBlame(cx, &report);
   1.834 +
   1.835 +    if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber,
   1.836 +                                 &message, &report, argumentsType, ap)) {
   1.837 +        return false;
   1.838 +    }
   1.839 +
   1.840 +    ReportError(cx, message, &report, callback, userRef);
   1.841 +
   1.842 +    js_free(message);
   1.843 +    if (report.messageArgs) {
   1.844 +        /*
   1.845 +         * js_ExpandErrorArguments owns its messageArgs only if it had to
   1.846 +         * inflate the arguments (from regular |char *|s).
   1.847 +         */
   1.848 +        if (argumentsType == ArgumentsAreASCII) {
   1.849 +            int i = 0;
   1.850 +            while (report.messageArgs[i])
   1.851 +                js_free((void *)report.messageArgs[i++]);
   1.852 +        }
   1.853 +        js_free((void *)report.messageArgs);
   1.854 +    }
   1.855 +    js_free((void *)report.ucmessage);
   1.856 +
   1.857 +    return warning;
   1.858 +}
   1.859 +
   1.860 +bool
   1.861 +js_ReportErrorNumberUCArray(JSContext *cx, unsigned flags, JSErrorCallback callback,
   1.862 +                            void *userRef, const unsigned errorNumber,
   1.863 +                            const jschar **args)
   1.864 +{
   1.865 +    if (checkReportFlags(cx, &flags))
   1.866 +        return true;
   1.867 +    bool warning = JSREPORT_IS_WARNING(flags);
   1.868 +
   1.869 +    JSErrorReport report;
   1.870 +    PodZero(&report);
   1.871 +    report.flags = flags;
   1.872 +    report.errorNumber = errorNumber;
   1.873 +    PopulateReportBlame(cx, &report);
   1.874 +    report.messageArgs = args;
   1.875 +
   1.876 +    char *message;
   1.877 +    va_list dummy;
   1.878 +    if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber,
   1.879 +                                 &message, &report, ArgumentsAreUnicode, dummy)) {
   1.880 +        return false;
   1.881 +    }
   1.882 +
   1.883 +    ReportError(cx, message, &report, callback, userRef);
   1.884 +
   1.885 +    js_free(message);
   1.886 +    js_free((void *)report.ucmessage);
   1.887 +
   1.888 +    return warning;
   1.889 +}
   1.890 +
   1.891 +void
   1.892 +js::CallErrorReporter(JSContext *cx, const char *message, JSErrorReport *reportp)
   1.893 +{
   1.894 +    JS_ASSERT(message);
   1.895 +    JS_ASSERT(reportp);
   1.896 +
   1.897 +    // If debugErrorHook is present, give it a chance to veto sending the error
   1.898 +    // on to the regular ErrorReporter.
   1.899 +    if (cx->errorReporter) {
   1.900 +        JSDebugErrorHook hook = cx->runtime()->debugHooks.debugErrorHook;
   1.901 +        if (hook && !hook(cx, message, reportp, cx->runtime()->debugHooks.debugErrorHookData))
   1.902 +            return;
   1.903 +    }
   1.904 +
   1.905 +    if (JSErrorReporter onError = cx->errorReporter)
   1.906 +        onError(cx, message, reportp);
   1.907 +}
   1.908 +
   1.909 +void
   1.910 +js_ReportIsNotDefined(JSContext *cx, const char *name)
   1.911 +{
   1.912 +    JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_DEFINED, name);
   1.913 +}
   1.914 +
   1.915 +bool
   1.916 +js_ReportIsNullOrUndefined(JSContext *cx, int spindex, HandleValue v,
   1.917 +                           HandleString fallback)
   1.918 +{
   1.919 +    char *bytes;
   1.920 +    bool ok;
   1.921 +
   1.922 +    bytes = DecompileValueGenerator(cx, spindex, v, fallback);
   1.923 +    if (!bytes)
   1.924 +        return false;
   1.925 +
   1.926 +    if (strcmp(bytes, js_undefined_str) == 0 ||
   1.927 +        strcmp(bytes, js_null_str) == 0) {
   1.928 +        ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
   1.929 +                                          js_GetErrorMessage, nullptr,
   1.930 +                                          JSMSG_NO_PROPERTIES, bytes,
   1.931 +                                          nullptr, nullptr);
   1.932 +    } else if (v.isUndefined()) {
   1.933 +        ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
   1.934 +                                          js_GetErrorMessage, nullptr,
   1.935 +                                          JSMSG_UNEXPECTED_TYPE, bytes,
   1.936 +                                          js_undefined_str, nullptr);
   1.937 +    } else {
   1.938 +        JS_ASSERT(v.isNull());
   1.939 +        ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
   1.940 +                                          js_GetErrorMessage, nullptr,
   1.941 +                                          JSMSG_UNEXPECTED_TYPE, bytes,
   1.942 +                                          js_null_str, nullptr);
   1.943 +    }
   1.944 +
   1.945 +    js_free(bytes);
   1.946 +    return ok;
   1.947 +}
   1.948 +
   1.949 +void
   1.950 +js_ReportMissingArg(JSContext *cx, HandleValue v, unsigned arg)
   1.951 +{
   1.952 +    char argbuf[11];
   1.953 +    char *bytes;
   1.954 +    RootedAtom atom(cx);
   1.955 +
   1.956 +    JS_snprintf(argbuf, sizeof argbuf, "%u", arg);
   1.957 +    bytes = nullptr;
   1.958 +    if (IsFunctionObject(v)) {
   1.959 +        atom = v.toObject().as<JSFunction>().atom();
   1.960 +        bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK,
   1.961 +                                        v, atom);
   1.962 +        if (!bytes)
   1.963 +            return;
   1.964 +    }
   1.965 +    JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
   1.966 +                         JSMSG_MISSING_FUN_ARG, argbuf,
   1.967 +                         bytes ? bytes : "");
   1.968 +    js_free(bytes);
   1.969 +}
   1.970 +
   1.971 +bool
   1.972 +js_ReportValueErrorFlags(JSContext *cx, unsigned flags, const unsigned errorNumber,
   1.973 +                         int spindex, HandleValue v, HandleString fallback,
   1.974 +                         const char *arg1, const char *arg2)
   1.975 +{
   1.976 +    char *bytes;
   1.977 +    bool ok;
   1.978 +
   1.979 +    JS_ASSERT(js_ErrorFormatString[errorNumber].argCount >= 1);
   1.980 +    JS_ASSERT(js_ErrorFormatString[errorNumber].argCount <= 3);
   1.981 +    bytes = DecompileValueGenerator(cx, spindex, v, fallback);
   1.982 +    if (!bytes)
   1.983 +        return false;
   1.984 +
   1.985 +    ok = JS_ReportErrorFlagsAndNumber(cx, flags, js_GetErrorMessage,
   1.986 +                                      nullptr, errorNumber, bytes, arg1, arg2);
   1.987 +    js_free(bytes);
   1.988 +    return ok;
   1.989 +}
   1.990 +
   1.991 +const JSErrorFormatString js_ErrorFormatString[JSErr_Limit] = {
   1.992 +#define MSG_DEF(name, number, count, exception, format) \
   1.993 +    { format, count, exception } ,
   1.994 +#include "js.msg"
   1.995 +#undef MSG_DEF
   1.996 +};
   1.997 +
   1.998 +JS_FRIEND_API(const JSErrorFormatString *)
   1.999 +js_GetErrorMessage(void *userRef, const char *locale, const unsigned errorNumber)
  1.1000 +{
  1.1001 +    if ((errorNumber > 0) && (errorNumber < JSErr_Limit))
  1.1002 +        return &js_ErrorFormatString[errorNumber];
  1.1003 +    return nullptr;
  1.1004 +}
  1.1005 +
  1.1006 +bool
  1.1007 +js::InvokeInterruptCallback(JSContext *cx)
  1.1008 +{
  1.1009 +    JS_ASSERT_REQUEST_DEPTH(cx);
  1.1010 +
  1.1011 +    JSRuntime *rt = cx->runtime();
  1.1012 +    JS_ASSERT(rt->interrupt);
  1.1013 +
  1.1014 +    // Reset the callback counter first, then run GC and yield. If another
  1.1015 +    // thread is racing us here we will accumulate another callback request
  1.1016 +    // which will be serviced at the next opportunity.
  1.1017 +    rt->interrupt = false;
  1.1018 +
  1.1019 +    // IonMonkey sets its stack limit to UINTPTR_MAX to trigger interrupt
  1.1020 +    // callbacks.
  1.1021 +    rt->resetJitStackLimit();
  1.1022 +
  1.1023 +    js::gc::GCIfNeeded(cx);
  1.1024 +
  1.1025 +#ifdef JS_ION
  1.1026 +#ifdef JS_THREADSAFE
  1.1027 +    rt->interruptPar = false;
  1.1028 +#endif
  1.1029 +
  1.1030 +    // A worker thread may have requested an interrupt after finishing an Ion
  1.1031 +    // compilation.
  1.1032 +    jit::AttachFinishedCompilations(cx);
  1.1033 +#endif
  1.1034 +
  1.1035 +    // Important: Additional callbacks can occur inside the callback handler
  1.1036 +    // if it re-enters the JS engine. The embedding must ensure that the
  1.1037 +    // callback is disconnected before attempting such re-entry.
  1.1038 +    JSInterruptCallback cb = cx->runtime()->interruptCallback;
  1.1039 +    if (!cb || cb(cx))
  1.1040 +        return true;
  1.1041 +
  1.1042 +    // No need to set aside any pending exception here: ComputeStackString
  1.1043 +    // already does that.
  1.1044 +    Rooted<JSString*> stack(cx, ComputeStackString(cx));
  1.1045 +    const jschar *chars = stack ? stack->getCharsZ(cx) : nullptr;
  1.1046 +    if (!chars)
  1.1047 +        chars = MOZ_UTF16("(stack not available)");
  1.1048 +    JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_WARNING, js_GetErrorMessage, nullptr,
  1.1049 +                                   JSMSG_TERMINATED, chars);
  1.1050 +
  1.1051 +    return false;
  1.1052 +}
  1.1053 +
  1.1054 +bool
  1.1055 +js::HandleExecutionInterrupt(JSContext *cx)
  1.1056 +{
  1.1057 +    if (cx->runtime()->interrupt)
  1.1058 +        return InvokeInterruptCallback(cx);
  1.1059 +    return true;
  1.1060 +}
  1.1061 +
  1.1062 +ThreadSafeContext::ThreadSafeContext(JSRuntime *rt, PerThreadData *pt, ContextKind kind)
  1.1063 +  : ContextFriendFields(rt),
  1.1064 +    contextKind_(kind),
  1.1065 +    perThreadData(pt),
  1.1066 +    allocator_(nullptr)
  1.1067 +{
  1.1068 +}
  1.1069 +
  1.1070 +bool
  1.1071 +ThreadSafeContext::isForkJoinContext() const
  1.1072 +{
  1.1073 +    return contextKind_ == Context_ForkJoin;
  1.1074 +}
  1.1075 +
  1.1076 +ForkJoinContext *
  1.1077 +ThreadSafeContext::asForkJoinContext()
  1.1078 +{
  1.1079 +    JS_ASSERT(isForkJoinContext());
  1.1080 +    return reinterpret_cast<ForkJoinContext *>(this);
  1.1081 +}
  1.1082 +
  1.1083 +void
  1.1084 +ThreadSafeContext::recoverFromOutOfMemory()
  1.1085 +{
  1.1086 +    // If this is not a JSContext, there's nothing to do.
  1.1087 +    if (JSContext *maybecx = maybeJSContext()) {
  1.1088 +        if (maybecx->isExceptionPending()) {
  1.1089 +            MOZ_ASSERT(maybecx->isThrowingOutOfMemory());
  1.1090 +            maybecx->clearPendingException();
  1.1091 +        } else {
  1.1092 +            MOZ_ASSERT(maybecx->runtime()->hadOutOfMemory);
  1.1093 +        }
  1.1094 +    }
  1.1095 +}
  1.1096 +
  1.1097 +JSContext::JSContext(JSRuntime *rt)
  1.1098 +  : ExclusiveContext(rt, &rt->mainThread, Context_JS),
  1.1099 +    throwing(false),
  1.1100 +    unwrappedException_(UndefinedValue()),
  1.1101 +    options_(),
  1.1102 +    reportGranularity(JS_DEFAULT_JITREPORT_GRANULARITY),
  1.1103 +    resolvingList(nullptr),
  1.1104 +    generatingError(false),
  1.1105 +    savedFrameChains_(),
  1.1106 +    defaultCompartmentObject_(nullptr),
  1.1107 +    cycleDetectorSet(MOZ_THIS_IN_INITIALIZER_LIST()),
  1.1108 +    errorReporter(nullptr),
  1.1109 +    data(nullptr),
  1.1110 +    data2(nullptr),
  1.1111 +#ifdef JS_THREADSAFE
  1.1112 +    outstandingRequests(0),
  1.1113 +#endif
  1.1114 +    iterValue(MagicValue(JS_NO_ITER_VALUE)),
  1.1115 +    jitIsBroken(false),
  1.1116 +#ifdef MOZ_TRACE_JSCALLS
  1.1117 +    functionCallback(nullptr),
  1.1118 +#endif
  1.1119 +    innermostGenerator_(nullptr)
  1.1120 +{
  1.1121 +#ifdef DEBUG
  1.1122 +    stackIterAssertionEnabled = true;
  1.1123 +#endif
  1.1124 +
  1.1125 +    JS_ASSERT(static_cast<ContextFriendFields*>(this) ==
  1.1126 +              ContextFriendFields::get(this));
  1.1127 +}
  1.1128 +
  1.1129 +JSContext::~JSContext()
  1.1130 +{
  1.1131 +    /* Free the stuff hanging off of cx. */
  1.1132 +    JS_ASSERT(!resolvingList);
  1.1133 +}
  1.1134 +
  1.1135 +bool
  1.1136 +JSContext::getPendingException(MutableHandleValue rval)
  1.1137 +{
  1.1138 +    JS_ASSERT(throwing);
  1.1139 +    rval.set(unwrappedException_);
  1.1140 +    if (IsAtomsCompartment(compartment()))
  1.1141 +        return true;
  1.1142 +    clearPendingException();
  1.1143 +    if (!compartment()->wrap(this, rval))
  1.1144 +        return false;
  1.1145 +    assertSameCompartment(this, rval);
  1.1146 +    setPendingException(rval);
  1.1147 +    return true;
  1.1148 +}
  1.1149 +
  1.1150 +bool
  1.1151 +JSContext::isThrowingOutOfMemory()
  1.1152 +{
  1.1153 +    return throwing && unwrappedException_ == StringValue(names().outOfMemory);
  1.1154 +}
  1.1155 +
  1.1156 +void
  1.1157 +JSContext::enterGenerator(JSGenerator *gen)
  1.1158 +{
  1.1159 +    JS_ASSERT(!gen->prevGenerator);
  1.1160 +    gen->prevGenerator = innermostGenerator_;
  1.1161 +    innermostGenerator_ = gen;
  1.1162 +}
  1.1163 +
  1.1164 +void
  1.1165 +JSContext::leaveGenerator(JSGenerator *gen)
  1.1166 +{
  1.1167 +    JS_ASSERT(innermostGenerator_ == gen);
  1.1168 +    innermostGenerator_ = innermostGenerator_->prevGenerator;
  1.1169 +    gen->prevGenerator = nullptr;
  1.1170 +}
  1.1171 +
  1.1172 +
  1.1173 +bool
  1.1174 +JSContext::runningWithTrustedPrincipals() const
  1.1175 +{
  1.1176 +    return !compartment() || compartment()->principals == runtime()->trustedPrincipals();
  1.1177 +}
  1.1178 +
  1.1179 +bool
  1.1180 +JSContext::saveFrameChain()
  1.1181 +{
  1.1182 +    if (!savedFrameChains_.append(SavedFrameChain(compartment(), enterCompartmentDepth_)))
  1.1183 +        return false;
  1.1184 +
  1.1185 +    if (Activation *act = mainThread().activation())
  1.1186 +        act->saveFrameChain();
  1.1187 +
  1.1188 +    setCompartment(nullptr);
  1.1189 +    enterCompartmentDepth_ = 0;
  1.1190 +
  1.1191 +    return true;
  1.1192 +}
  1.1193 +
  1.1194 +void
  1.1195 +JSContext::restoreFrameChain()
  1.1196 +{
  1.1197 +    JS_ASSERT(enterCompartmentDepth_ == 0); // We're about to clobber it, and it
  1.1198 +                                            // will be wrong forevermore.
  1.1199 +    SavedFrameChain sfc = savedFrameChains_.popCopy();
  1.1200 +    setCompartment(sfc.compartment);
  1.1201 +    enterCompartmentDepth_ = sfc.enterCompartmentCount;
  1.1202 +
  1.1203 +    if (Activation *act = mainThread().activation())
  1.1204 +        act->restoreFrameChain();
  1.1205 +}
  1.1206 +
  1.1207 +bool
  1.1208 +JSContext::currentlyRunning() const
  1.1209 +{
  1.1210 +    for (ActivationIterator iter(runtime()); !iter.done(); ++iter) {
  1.1211 +        if (iter->cx() == this) {
  1.1212 +            if (iter->hasSavedFrameChain())
  1.1213 +                return false;
  1.1214 +            return true;
  1.1215 +        }
  1.1216 +    }
  1.1217 +
  1.1218 +    return false;
  1.1219 +}
  1.1220 +
  1.1221 +static bool
  1.1222 +ComputeIsJITBroken()
  1.1223 +{
  1.1224 +#if !defined(ANDROID) || defined(GONK)
  1.1225 +    return false;
  1.1226 +#else  // ANDROID
  1.1227 +    if (getenv("JS_IGNORE_JIT_BROKENNESS")) {
  1.1228 +        return false;
  1.1229 +    }
  1.1230 +
  1.1231 +    std::string line;
  1.1232 +
  1.1233 +    // Check for the known-bad kernel version (2.6.29).
  1.1234 +    std::ifstream osrelease("/proc/sys/kernel/osrelease");
  1.1235 +    std::getline(osrelease, line);
  1.1236 +    __android_log_print(ANDROID_LOG_INFO, "Gecko", "Detected osrelease `%s'",
  1.1237 +                        line.c_str());
  1.1238 +
  1.1239 +    if (line.npos == line.find("2.6.29")) {
  1.1240 +        // We're using something other than 2.6.29, so the JITs should work.
  1.1241 +        __android_log_print(ANDROID_LOG_INFO, "Gecko", "JITs are not broken");
  1.1242 +        return false;
  1.1243 +    }
  1.1244 +
  1.1245 +    // We're using 2.6.29, and this causes trouble with the JITs on i9000.
  1.1246 +    line = "";
  1.1247 +    bool broken = false;
  1.1248 +    std::ifstream cpuinfo("/proc/cpuinfo");
  1.1249 +    do {
  1.1250 +        if (0 == line.find("Hardware")) {
  1.1251 +            static const char* const blacklist[] = {
  1.1252 +                "SCH-I400",     // Samsung Continuum
  1.1253 +                "SGH-T959",     // Samsung i9000, Vibrant device
  1.1254 +                "SGH-I897",     // Samsung i9000, Captivate device
  1.1255 +                "SCH-I500",     // Samsung i9000, Fascinate device
  1.1256 +                "SPH-D700",     // Samsung i9000, Epic device
  1.1257 +                "GT-I9000",     // Samsung i9000, UK/Europe device
  1.1258 +                nullptr
  1.1259 +            };
  1.1260 +            for (const char* const* hw = &blacklist[0]; *hw; ++hw) {
  1.1261 +                if (line.npos != line.find(*hw)) {
  1.1262 +                    __android_log_print(ANDROID_LOG_INFO, "Gecko",
  1.1263 +                                        "Blacklisted device `%s'", *hw);
  1.1264 +                    broken = true;
  1.1265 +                    break;
  1.1266 +                }
  1.1267 +            }
  1.1268 +            break;
  1.1269 +        }
  1.1270 +        std::getline(cpuinfo, line);
  1.1271 +    } while(!cpuinfo.fail() && !cpuinfo.eof());
  1.1272 +
  1.1273 +    __android_log_print(ANDROID_LOG_INFO, "Gecko", "JITs are %sbroken",
  1.1274 +                        broken ? "" : "not ");
  1.1275 +
  1.1276 +    return broken;
  1.1277 +#endif  // ifndef ANDROID
  1.1278 +}
  1.1279 +
  1.1280 +static bool
  1.1281 +IsJITBrokenHere()
  1.1282 +{
  1.1283 +    static bool computedIsBroken = false;
  1.1284 +    static bool isBroken = false;
  1.1285 +    if (!computedIsBroken) {
  1.1286 +        isBroken = ComputeIsJITBroken();
  1.1287 +        computedIsBroken = true;
  1.1288 +    }
  1.1289 +    return isBroken;
  1.1290 +}
  1.1291 +
  1.1292 +void
  1.1293 +JSContext::updateJITEnabled()
  1.1294 +{
  1.1295 +    jitIsBroken = IsJITBrokenHere();
  1.1296 +}
  1.1297 +
  1.1298 +size_t
  1.1299 +JSContext::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
  1.1300 +{
  1.1301 +    /*
  1.1302 +     * There are other JSContext members that could be measured; the following
  1.1303 +     * ones have been found by DMD to be worth measuring.  More stuff may be
  1.1304 +     * added later.
  1.1305 +     */
  1.1306 +    return mallocSizeOf(this) + cycleDetectorSet.sizeOfExcludingThis(mallocSizeOf);
  1.1307 +}
  1.1308 +
  1.1309 +void
  1.1310 +JSContext::mark(JSTracer *trc)
  1.1311 +{
  1.1312 +    /* Stack frames and slots are traced by StackSpace::mark. */
  1.1313 +
  1.1314 +    /* Mark other roots-by-definition in the JSContext. */
  1.1315 +    if (defaultCompartmentObject_)
  1.1316 +        MarkObjectRoot(trc, &defaultCompartmentObject_, "default compartment object");
  1.1317 +    if (isExceptionPending())
  1.1318 +        MarkValueRoot(trc, &unwrappedException_, "unwrapped exception");
  1.1319 +
  1.1320 +    TraceCycleDetectionSet(trc, cycleDetectorSet);
  1.1321 +
  1.1322 +    MarkValueRoot(trc, &iterValue, "iterValue");
  1.1323 +}
  1.1324 +
  1.1325 +void *
  1.1326 +ThreadSafeContext::stackLimitAddressForJitCode(StackKind kind)
  1.1327 +{
  1.1328 +#ifdef JS_ARM_SIMULATOR
  1.1329 +    return runtime_->mainThread.addressOfSimulatorStackLimit();
  1.1330 +#endif
  1.1331 +    return stackLimitAddress(kind);
  1.1332 +}
  1.1333 +
  1.1334 +JSVersion
  1.1335 +JSContext::findVersion() const
  1.1336 +{
  1.1337 +    if (JSScript *script = currentScript(nullptr, ALLOW_CROSS_COMPARTMENT))
  1.1338 +        return script->getVersion();
  1.1339 +
  1.1340 +    if (compartment() && compartment()->options().version() != JSVERSION_UNKNOWN)
  1.1341 +        return compartment()->options().version();
  1.1342 +
  1.1343 +    return runtime()->defaultVersion();
  1.1344 +}
  1.1345 +
  1.1346 +#if defined JS_THREADSAFE && defined DEBUG
  1.1347 +
  1.1348 +JS::AutoCheckRequestDepth::AutoCheckRequestDepth(JSContext *cx)
  1.1349 +    : cx(cx)
  1.1350 +{
  1.1351 +    JS_ASSERT(cx->runtime()->requestDepth || cx->runtime()->isHeapBusy());
  1.1352 +    JS_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
  1.1353 +    cx->runtime()->checkRequestDepth++;
  1.1354 +}
  1.1355 +
  1.1356 +JS::AutoCheckRequestDepth::AutoCheckRequestDepth(ContextFriendFields *cxArg)
  1.1357 +    : cx(static_cast<ThreadSafeContext *>(cxArg)->maybeJSContext())
  1.1358 +{
  1.1359 +    if (cx) {
  1.1360 +        JS_ASSERT(cx->runtime()->requestDepth || cx->runtime()->isHeapBusy());
  1.1361 +        JS_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
  1.1362 +        cx->runtime()->checkRequestDepth++;
  1.1363 +    }
  1.1364 +}
  1.1365 +
  1.1366 +JS::AutoCheckRequestDepth::~AutoCheckRequestDepth()
  1.1367 +{
  1.1368 +    if (cx) {
  1.1369 +        JS_ASSERT(cx->runtime()->checkRequestDepth != 0);
  1.1370 +        cx->runtime()->checkRequestDepth--;
  1.1371 +    }
  1.1372 +}
  1.1373 +
  1.1374 +#endif
  1.1375 +
  1.1376 +#ifdef JS_CRASH_DIAGNOSTICS
  1.1377 +void CompartmentChecker::check(InterpreterFrame *fp)
  1.1378 +{
  1.1379 +    if (fp)
  1.1380 +        check(fp->scopeChain());
  1.1381 +}
  1.1382 +
  1.1383 +void CompartmentChecker::check(AbstractFramePtr frame)
  1.1384 +{
  1.1385 +    if (frame)
  1.1386 +        check(frame.scopeChain());
  1.1387 +}
  1.1388 +#endif
  1.1389 +
  1.1390 +void
  1.1391 +js::CrashAtUnhandlableOOM(const char *reason)
  1.1392 +{
  1.1393 +    char msgbuf[1024];
  1.1394 +    JS_snprintf(msgbuf, sizeof(msgbuf), "[unhandlable oom] %s", reason);
  1.1395 +    MOZ_ReportAssertionFailure(msgbuf, __FILE__, __LINE__);
  1.1396 +    MOZ_CRASH();
  1.1397 +}

mercurial