Ejemplo n.º 1
0
void
ErrorReport::ReportAddonExceptionToTelementry(JSContext* cx)
{
    MOZ_ASSERT(exnObject);
    RootedObject unwrapped(cx, UncheckedUnwrap(exnObject));
    MOZ_ASSERT(unwrapped, "UncheckedUnwrap failed?");

    // There is not much we can report if the exception is not an ErrorObject, let's ignore those.
    if (!unwrapped->is<ErrorObject>())
        return;

    Rooted<ErrorObject*> errObj(cx, &unwrapped->as<ErrorObject>());
    RootedObject stack(cx, errObj->stack());

    // Let's ignore TOP level exceptions. For regular add-ons those will not be reported anyway,
    // for SDK based once it should not be a valid case either.
    // At this point the frame stack is unwound but the exception object stored the stack so let's
    // use that for getting the function name.
    if (!stack)
        return;

    JSCompartment* comp = stack->compartment();
    JSAddonId* addonId = comp->creationOptions().addonIdOrNull();

    // We only want to send the report if the scope that just have thrown belongs to an add-on.
    // Let's check the compartment of the youngest function on the stack, to determine that.
    if (!addonId)
        return;

    RootedString funnameString(cx);
    JS::SavedFrameResult result = GetSavedFrameFunctionDisplayName(cx, stack, &funnameString);
    // AccessDenied should never be the case here for add-ons but let's not risk it.
    JSAutoByteString bytes;
    const char* funname = nullptr;
    bool denied = result == JS::SavedFrameResult::AccessDenied;
    funname = denied ? "unknown"
                     : funnameString ? AtomToPrintableString(cx,
                                                             &funnameString->asAtom(),
                                                             &bytes)
                                     : "anonymous";

    UniqueChars addonIdChars(JS_EncodeString(cx, addonId));

    const char* filename = nullptr;
    if (reportp && reportp->filename) {
        filename = strrchr(reportp->filename, '/');
        if (filename)
            filename++;
    }
    if (!filename) {
        filename = "FILE_NOT_FOUND";
    }
    char histogramKey[64];
    SprintfLiteral(histogramKey, "%s %s %s %u",
                   addonIdChars.get(),
                   funname,
                   filename,
                   (reportp ? reportp->lineno : 0) );
    cx->runtime()->addTelemetry(JS_TELEMETRY_ADDON_EXCEPTIONS, 1, histogramKey);
}
IonBailoutIterator::IonBailoutIterator(const IonActivationIterator &activations,
                                       BailoutStack *bailout)
  : IonFrameIterator(activations),
    machine_(bailout->machine())
{
    uint8_t *sp = bailout->parentStackPointer();
    uint8_t *fp = sp + bailout->frameSize();

    current_ = fp;
    type_ = IonFrame_OptimizedJS;
    topFrameSize_ = current_ - sp;
    topIonScript_ = script()->ion;

    if (bailout->frameClass() == FrameSizeClass::None()) {
        snapshotOffset_ = bailout->snapshotOffset();
        return;
    }

    // Compute the snapshot offset from the bailout ID.
    IonActivation *activation = activations.activation();
    JSCompartment *jsCompartment = activation->compartment();
    IonCompartment *ionCompartment = jsCompartment->ionCompartment();
    IonCode *code = ionCompartment->getBailoutTable(bailout->frameClass());
    uintptr_t tableOffset = bailout->tableOffset();
    uintptr_t tableStart = reinterpret_cast<uintptr_t>(code->raw());

    JS_ASSERT(tableOffset >= tableStart &&
              tableOffset < tableStart + code->instructionsSize());
    JS_ASSERT((tableOffset - tableStart) % BAILOUT_TABLE_ENTRY_SIZE == 0);

    uint32_t bailoutId = ((tableOffset - tableStart) / BAILOUT_TABLE_ENTRY_SIZE) - 1;
    JS_ASSERT(bailoutId < BAILOUT_TABLE_SIZE);

    snapshotOffset_ = topIonScript_->bailoutToSnapshot(bailoutId);
}
Ejemplo n.º 3
0
/*
 * Rewrap *idp and the fields of *desc for the current compartment.  Also:
 * defining a property on a proxy requires pd_ to contain a descriptor object,
 * so reconstitute desc->pd_ if needed.
 */
bool
PropDesc::wrapInto(JSContext *cx, HandleObject obj, const jsid &id, jsid *wrappedId,
                   PropDesc *desc) const
{
    MOZ_ASSERT(!isUndefined());

    JSCompartment *comp = cx->compartment;

    *wrappedId = id;
    if (!comp->wrapId(cx, wrappedId))
        return false;

    *desc = *this;
    RootedValue value(cx, desc->value_);
    RootedValue get(cx, desc->get_);
    RootedValue set(cx, desc->set_);

    if (!comp->wrap(cx, &value) || !comp->wrap(cx, &get) || !comp->wrap(cx, &set))
        return false;

    desc->value_ = value;
    desc->get_ = get;
    desc->set_ = set;
    return !obj->isProxy() || desc->makeObject(cx);
}
Ejemplo n.º 4
0
bool
JSCompartment::wrap(JSContext *cx, MutableHandle<PropDesc> desc)
{
    if (desc.isUndefined())
        return true;

    JSCompartment *comp = cx->compartment();

    if (desc.hasValue()) {
        RootedValue value(cx, desc.value());
        if (!comp->wrap(cx, &value))
            return false;
        desc.setValue(value);
    }
    if (desc.hasGet()) {
        RootedValue get(cx, desc.getterValue());
        if (!comp->wrap(cx, &get))
            return false;
        desc.setGetter(get);
    }
    if (desc.hasSet()) {
        RootedValue set(cx, desc.setterValue());
        if (!comp->wrap(cx, &set))
            return false;
        desc.setSetter(set);
    }
    return true;
}
Ejemplo n.º 5
0
/*
 * Move an IonBuilder for which compilation has either finished, failed, or
 * been cancelled into the Ion compartment's finished compilations list.
 * All off thread compilations which are started must eventually be finished.
 */
static void
FinishOffThreadIonCompile(ion::IonBuilder *builder)
{
    JSCompartment *compartment = builder->script->compartment();
    JS_ASSERT(compartment->rt->workerThreadState->isLocked());

    compartment->ionCompartment()->finishedOffThreadCompilations().append(builder);
}
js::NukeCrossCompartmentWrapper(JSContext* cx, JSObject* wrapper)
{
    JSCompartment* comp = wrapper->compartment();
    auto ptr = comp->lookupWrapper(ObjectValue(*Wrapper::wrappedObject(wrapper)));
    if (ptr)
        comp->removeWrapper(ptr);
    NukeRemovedCrossCompartmentWrapper(cx, wrapper);
}
Ejemplo n.º 7
0
static bool
IsMarked(T **thingp)
{
    JS_ASSERT(thingp);
    JS_ASSERT(*thingp);
    JSCompartment *c = (*thingp)->compartment();
    if (!c->isCollecting() || c->isGCFinished())
        return true;
    return (*thingp)->isMarked();
}
Ejemplo n.º 8
0
// Given a cross-compartment wrapper |wobj|, update it to point to
// |newTarget|. This recomputes the wrapper with JS_WrapValue, and thus can be
// useful even if wrapper already points to newTarget.
bool
js::RemapWrapper(JSContext *cx, JSObject *wobj, JSObject *newTarget)
{
    JS_ASSERT(IsCrossCompartmentWrapper(wobj));
    JS_ASSERT(!IsCrossCompartmentWrapper(newTarget));
    JSObject *origTarget = Wrapper::wrappedObject(wobj);
    JS_ASSERT(origTarget);
    Value origv = ObjectValue(*origTarget);
    JSCompartment *wcompartment = wobj->compartment();
    WrapperMap &pmap = wcompartment->crossCompartmentWrappers;

    // If we're mapping to a different target (as opposed to just recomputing
    // for the same target), we must not have an existing wrapper for the new
    // target, otherwise this will break.
    JS_ASSERT_IF(origTarget != newTarget, !pmap.has(ObjectValue(*newTarget)));

    // The old value should still be in the cross-compartment wrapper map, and
    // the lookup should return wobj.
    JS_ASSERT(&pmap.lookup(origv)->value.unsafeGet()->toObject() == wobj);
    pmap.remove(origv);

    // When we remove origv from the wrapper map, its wrapper, wobj, must
    // immediately cease to be a cross-compartment wrapper. Neuter it.
    NukeCrossCompartmentWrapper(cx, wobj);

    // First, we wrap it in the new compartment. We try to use the existing
    // wrapper, |wobj|, since it's been nuked anyway. The wrap() function has
    // the choice to reuse |wobj| or not.
    JSObject *tobj = newTarget;
    AutoCompartment ac(cx, wobj);
    if (!wcompartment->wrap(cx, &tobj, wobj))
        MOZ_CRASH();

    // If wrap() reused |wobj|, it will have overwritten it and returned with
    // |tobj == wobj|. Otherwise, |tobj| will point to a new wrapper and |wobj|
    // will still be nuked. In the latter case, we replace |wobj| with the
    // contents of the new wrapper in |tobj|.
    if (tobj != wobj) {
        // Now, because we need to maintain object identity, we do a brain
        // transplant on the old object so that it contains the contents of the
        // new one.
        if (!wobj->swap(cx, tobj))
            MOZ_CRASH();
    }

    // Before swapping, this wrapper came out of wrap(), which enforces the
    // invariant that the wrapper in the map points directly to the key.
    JS_ASSERT(Wrapper::wrappedObject(wobj) == newTarget);

    // Update the entry in the compartment's wrapper map to point to the old
    // wrapper, which has now been updated (via reuse or swap).
    pmap.put(ObjectValue(*newTarget), ObjectValue(*wobj));
    return true;
}
void
ForkJoinShared::transferArenasToCompartmentAndProcessGCRequests()
{
    JSCompartment *comp = cx_->compartment;
    for (unsigned i = 0; i < numSlices_; i++)
        comp->adoptWorkerAllocator(allocators_[i]);

    if (gcRequested_) {
        if (!gcZone_)
            TriggerGC(cx_->runtime, gcReason_);
        else
            TriggerZoneGC(gcZone_, gcReason_);
        gcRequested_ = false;
        gcZone_ = NULL;
    }
}
Ejemplo n.º 10
0
// Given a cross-compartment wrapper |wobj|, update it to point to
// |newTarget|. This recomputes the wrapper with JS_WrapValue, and thus can be
// useful even if wrapper already points to newTarget.
bool
js::RemapWrapper(JSContext *cx, JSObject *wobj, JSObject *newTarget)
{
    JS_ASSERT(IsCrossCompartmentWrapper(wobj));
    JS_ASSERT(!IsCrossCompartmentWrapper(newTarget));
    JSObject *origTarget = Wrapper::wrappedObject(wobj);
    JS_ASSERT(origTarget);
    Value origv = ObjectValue(*origTarget);
    JSCompartment *wcompartment = wobj->compartment();
    WrapperMap &pmap = wcompartment->crossCompartmentWrappers;

    // If we're mapping to a different target (as opposed to just recomputing
    // for the same target), we must not have an existing wrapper for the new
    // target, otherwise this will break.
    JS_ASSERT_IF(origTarget != newTarget, !pmap.has(ObjectValue(*newTarget)));

    // The old value should still be in the cross-compartment wrapper map, and
    // the lookup should return wobj.
    JS_ASSERT(&pmap.lookup(origv)->value.toObject() == wobj);
    pmap.remove(origv);

    // When we remove origv from the wrapper map, its wrapper, wobj, must
    // immediately cease to be a cross-compartment wrapper. Neuter it.
    NukeCrossCompartmentWrapper(wobj);

    // First, we wrap it in the new compartment. This will return
    // a new wrapper.
    AutoCompartment ac(cx, wobj);
    JSObject *tobj = newTarget;
    if (!ac.enter() || !wcompartment->wrap(cx, &tobj))
        return false;

    // Now, because we need to maintain object identity, we do a
    // brain transplant on the old object. At the same time, we
    // update the entry in the compartment's wrapper map to point
    // to the old wrapper.
    JS_ASSERT(tobj != wobj);
    if (!wobj->swap(cx, tobj))
        return false;

    // Before swapping, this wrapper came out of wrap(), which enforces the
    // invariant that the wrapper in the map points directly to the key.
    JS_ASSERT(Wrapper::wrappedObject(wobj) == newTarget);

    pmap.put(ObjectValue(*newTarget), ObjectValue(*wobj));
    return true;
}
Ejemplo n.º 11
0
GlobalObject*
GlobalObject::new_(JSContext* cx, const Class* clasp, JSPrincipals* principals,
                   JS::OnNewGlobalHookOption hookOption,
                   const JS::CompartmentOptions& options)
{
    MOZ_ASSERT(!cx->isExceptionPending());
    MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));

    JSRuntime* rt = cx->runtime();

    auto zoneSpecifier = options.creationOptions().zoneSpecifier();
    Zone* zone;
    if (zoneSpecifier == JS::SystemZone)
        zone = rt->gc.systemZone;
    else if (zoneSpecifier == JS::FreshZone)
        zone = nullptr;
    else
        zone = static_cast<Zone*>(options.creationOptions().zonePointer());

    JSCompartment* compartment = NewCompartment(cx, zone, principals, options);
    if (!compartment)
        return nullptr;

    // Lazily create the system zone.
    if (!rt->gc.systemZone && zoneSpecifier == JS::SystemZone) {
        rt->gc.systemZone = compartment->zone();
        rt->gc.systemZone->isSystem = true;
    }

    Rooted<GlobalObject*> global(cx);
    {
        AutoCompartment ac(cx, compartment);
        global = GlobalObject::createInternal(cx, clasp);
        if (!global)
            return nullptr;
    }

    if (hookOption == JS::FireOnNewGlobalHook)
        JS_FireOnNewGlobalObject(cx, global);

    return global;
}
Ejemplo n.º 12
0
/* static */ bool
DebuggerMemory::setTrackingAllocationSites(JSContext *cx, unsigned argc, Value *vp)
{
    THIS_DEBUGGER_MEMORY(cx, argc, vp, "(set trackingAllocationSites)", args, memory);
    if (!args.requireAtLeast(cx, "(set trackingAllocationSites)", 1))
        return false;

    Debugger *dbg = memory->getDebugger();
    bool enabling = ToBoolean(args[0]);

    if (enabling == dbg->trackingAllocationSites) {
        // Nothing to do here...
        args.rval().setUndefined();
        return true;
    }

    if (enabling) {
        for (GlobalObjectSet::Range r = dbg->debuggees.all(); !r.empty(); r.popFront()) {
            JSCompartment *compartment = r.front()->compartment();
            if (compartment->hasObjectMetadataCallback()) {
                JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
                                     JSMSG_OBJECT_METADATA_CALLBACK_ALREADY_SET);
                return false;
            }
        }
    }

    for (GlobalObjectSet::Range r = dbg->debuggees.all(); !r.empty(); r.popFront()) {
        if (enabling) {
            r.front()->compartment()->setObjectMetadataCallback(SavedStacksMetadataCallback);
        } else {
            r.front()->compartment()->forgetObjectMetadataCallback();
        }
    }

    if (!enabling)
        dbg->emptyAllocationsLog();

    dbg->trackingAllocationSites = enabling;
    args.rval().setUndefined();
    return true;
}
Ejemplo n.º 13
0
/*
 * Rewrap *idp and the fields of *desc for the current compartment.  Also:
 * defining a property on a proxy requires pd_ to contain a descriptor object,
 * so reconstitute desc->pd_ if needed.
 */
bool
PropDesc::wrapInto(JSContext *cx, JSObject *obj, const jsid &id, jsid *wrappedId,
                   PropDesc *desc) const
{
    MOZ_ASSERT(!isUndefined());

    JSCompartment *comp = cx->compartment;

    *wrappedId = id;
    if (!comp->wrapId(cx, wrappedId))
        return false;

    *desc = *this;
    if (!comp->wrap(cx, &desc->value_))
        return false;
    if (!comp->wrap(cx, &desc->get_))
        return false;
    if (!comp->wrap(cx, &desc->set_))
        return false;
    return !obj->isProxy() || desc->makeObject(cx);
}
Ejemplo n.º 14
0
// Given a cross-compartment wrapper |wobj|, update it to point to
// |newTarget|. This recomputes the wrapper with JS_WrapValue, and thus can be
// useful even if wrapper already points to newTarget.
// This operation crashes on failure rather than leaving the heap in an
// inconsistent state.
void
js::RemapWrapper(JSContext* cx, JSObject* wobjArg, JSObject* newTargetArg)
{
    RootedObject wobj(cx, wobjArg);
    RootedObject newTarget(cx, newTargetArg);
    MOZ_ASSERT(wobj->is<CrossCompartmentWrapperObject>());
    MOZ_ASSERT(!newTarget->is<CrossCompartmentWrapperObject>());
    JSObject* origTarget = Wrapper::wrappedObject(wobj);
    MOZ_ASSERT(origTarget);
    Value origv = ObjectValue(*origTarget);
    JSCompartment* wcompartment = wobj->compartment();

    AutoDisableProxyCheck adpc(cx->runtime());

    // If we're mapping to a different target (as opposed to just recomputing
    // for the same target), we must not have an existing wrapper for the new
    // target, otherwise this will break.
    MOZ_ASSERT_IF(origTarget != newTarget,
                  !wcompartment->lookupWrapper(ObjectValue(*newTarget)));

    // The old value should still be in the cross-compartment wrapper map, and
    // the lookup should return wobj.
    WrapperMap::Ptr p = wcompartment->lookupWrapper(origv);
    MOZ_ASSERT(&p->value().unsafeGet()->toObject() == wobj);
    wcompartment->removeWrapper(p);

    // When we remove origv from the wrapper map, its wrapper, wobj, must
    // immediately cease to be a cross-compartment wrapper. Nuke it.
    NukeCrossCompartmentWrapper(cx, wobj);

    // First, we wrap it in the new compartment. We try to use the existing
    // wrapper, |wobj|, since it's been nuked anyway. The wrap() function has
    // the choice to reuse |wobj| or not.
    RootedObject tobj(cx, newTarget);
    AutoCompartment ac(cx, wobj);
    if (!wcompartment->wrap(cx, &tobj, wobj))
        MOZ_CRASH();

    // If wrap() reused |wobj|, it will have overwritten it and returned with
    // |tobj == wobj|. Otherwise, |tobj| will point to a new wrapper and |wobj|
    // will still be nuked. In the latter case, we replace |wobj| with the
    // contents of the new wrapper in |tobj|.
    if (tobj != wobj) {
        // Now, because we need to maintain object identity, we do a brain
        // transplant on the old object so that it contains the contents of the
        // new one.
        if (!JSObject::swap(cx, wobj, tobj))
            MOZ_CRASH();
    }

    // Before swapping, this wrapper came out of wrap(), which enforces the
    // invariant that the wrapper in the map points directly to the key.
    MOZ_ASSERT(Wrapper::wrappedObject(wobj) == newTarget);

    // Update the entry in the compartment's wrapper map to point to the old
    // wrapper, which has now been updated (via reuse or swap).
    MOZ_ASSERT(wobj->is<WrapperObject>());
    if (!wcompartment->putWrapper(cx, CrossCompartmentKey(newTarget), ObjectValue(*wobj)))
        MOZ_CRASH();
}
Ejemplo n.º 15
0
/*
 * The given JSErrorReport object have been zeroed and must not outlive
 * cx->fp() (otherwise owned fields may become invalid).
 */
static void
PopulateReportBlame(JSContext* cx, JSErrorReport* report)
{
    JSCompartment* compartment = cx->compartment();
    if (!compartment)
        return;

    /*
     * Walk stack until we find a frame that is associated with a non-builtin
     * rather than a builtin frame and which we're allowed to know about.
     */
    NonBuiltinFrameIter iter(cx, compartment->principals());
    if (iter.done())
        return;

    report->filename = iter.filename();
    report->lineno = iter.computeLine(&report->column);
    // XXX: Make the column 1-based as in other browsers, instead of 0-based
    // which is how SpiderMonkey stores it internally. This will be
    // unnecessary once bug 1144340 is fixed.
    report->column++;
    report->isMuted = iter.mutedErrors();
}
Ejemplo n.º 16
0
static bool
ShouldMarkCrossCompartment(JSTracer *trc, RawObject src, Cell *cell)
{
    if (!IS_GC_MARKING_TRACER(trc))
        return true;

    JSCompartment *c = cell->compartment();
    uint32_t color = AsGCMarker(trc)->getMarkColor();

    JS_ASSERT(color == BLACK || color == GRAY);
    if (color == BLACK) {
        /*
         * Having black->gray edges violates our promise to the cycle
         * collector. This can happen if we're collecting a compartment and it
         * has an edge to an uncollected compartment: it's possible that the
         * source and destination of the cross-compartment edge should be gray,
         * but the source was marked black by the conservative scanner.
         */
        if (cell->isMarked(GRAY)) {
            JS_ASSERT(!cell->compartment()->isCollecting());
            trc->runtime->gcFoundBlackGrayEdges = true;
        }
        return c->isGCMarking();
    } else {
        if (c->isGCMarkingBlack()) {
            /*
             * The destination compartment is being not being marked gray now,
             * but it will be later, so record the cell so it can be marked gray
             * at the appropriate time.
             */
            if (!cell->isMarked())
                DelayCrossCompartmentGrayMarking(src);
            return false;
        }
        return c->isGCMarkingGray();
    }
}
Ejemplo n.º 17
0
Shape *
PropertyTree::getChild(JSContext *cx, Shape *parent_, uint32_t nfixed, const StackShape &child)
{
    Shape *shape = NULL;

    JS_ASSERT(parent_);

    /*
     * The property tree has extremely low fan-out below its root in
     * popular embeddings with real-world workloads. Patterns such as
     * defining closures that capture a constructor's environment as
     * getters or setters on the new object that is passed in as
     * |this| can significantly increase fan-out below the property
     * tree root -- see bug 335700 for details.
     */
    KidsPointer *kidp = &parent_->kids;
    if (kidp->isShape()) {
        Shape *kid = kidp->toShape();
        if (kid->matches(child))
            shape = kid;
    } else if (kidp->isHash()) {
        shape = *kidp->toHash()->lookup(child);
    } else {
        /* If kidp->isNull(), we always insert. */
    }

#ifdef JSGC_INCREMENTAL
    if (shape) {
        JSCompartment *comp = shape->compartment();
        if (comp->needsBarrier()) {
            /*
             * We need a read barrier for the shape tree, since these are weak
             * pointers.
             */
            Shape *tmp = shape;
            MarkShapeUnbarriered(comp->barrierTracer(), &tmp, "read barrier");
            JS_ASSERT(tmp == shape);
        } else if (comp->isGCSweeping() && !shape->isMarked() &&
                   !shape->arenaHeader()->allocatedDuringIncremental)
        {
            /*
             * The shape we've found is unreachable and due to be finalized, so
             * remove our weak reference to it and don't use it.
             */
            JS_ASSERT(parent_->isMarked());
            parent_->removeChild(shape);
            shape = NULL;
        }
    }
#endif

    if (shape)
        return shape;

    StackShape::AutoRooter childRoot(cx, &child);
    RootedShape parent(cx, parent_);

    shape = newShape(cx);
    if (!shape)
        return NULL;

    new (shape) Shape(child, nfixed);

    if (!insertChild(cx, parent, shape))
        return NULL;

    return shape;
}