Ejemplo n.º 1
0
void
GlobalHelperThreadState::mergeParseTaskCompartment(JSRuntime* rt, ParseTask* parseTask,
                                                   Handle<GlobalObject*> global,
                                                   JSCompartment* dest)
{
    // After we call LeaveParseTaskZone() it's not safe to GC until we have
    // finished merging the contents of the parse task's compartment into the
    // destination compartment.  Finish any ongoing incremental GC first and
    // assert that no allocation can occur.
    gc::AutoFinishGC finishGC(rt);
    JS::AutoAssertNoAlloc noAlloc(rt);

    LeaveParseTaskZone(rt, parseTask);

    {
        gc::ZoneCellIter iter(parseTask->cx->zone(), gc::AllocKind::OBJECT_GROUP);

        // Generator functions don't have Function.prototype as prototype but a
        // different function object, so the IdentifyStandardPrototype trick
        // below won't work.  Just special-case it.
        JSObject* parseTaskStarGenFunctionProto =
            parseTask->exclusiveContextGlobal->as<GlobalObject>().getStarGeneratorFunctionPrototype();

        // Point the prototypes of any objects in the script's compartment to refer
        // to the corresponding prototype in the new compartment. This will briefly
        // create cross compartment pointers, which will be fixed by the
        // MergeCompartments call below.
        for (; !iter.done(); iter.next()) {
            ObjectGroup* group = iter.get<ObjectGroup>();
            TaggedProto proto(group->proto());
            if (!proto.isObject())
                continue;

            JSObject* protoObj = proto.toObject();

            JSObject* newProto;
            if (protoObj == parseTaskStarGenFunctionProto) {
                newProto = global->getStarGeneratorFunctionPrototype();
            } else {
                JSProtoKey key = JS::IdentifyStandardPrototype(protoObj);
                if (key == JSProto_Null)
                    continue;

                MOZ_ASSERT(key == JSProto_Object || key == JSProto_Array ||
                           key == JSProto_Function || key == JSProto_RegExp ||
                           key == JSProto_Iterator);

                newProto = GetBuiltinPrototypePure(global, key);
            }

            MOZ_ASSERT(newProto);
            group->setProtoUnchecked(TaggedProto(newProto));
        }
    }

    // Move the parsed script and all its contents into the desired compartment.
    gc::MergeCompartments(parseTask->cx->compartment(), dest);
}
Ejemplo n.º 2
0
JSScript *
GlobalHelperThreadState::finishParseTask(JSContext *maybecx, JSRuntime *rt, void *token)
{
    ScopedJSDeletePtr<ParseTask> parseTask;

    // The token is a ParseTask* which should be in the finished list.
    // Find and remove its entry.
    {
        AutoLockHelperThreadState lock;
        ParseTaskVector &finished = parseFinishedList();
        for (size_t i = 0; i < finished.length(); i++) {
            if (finished[i] == token) {
                parseTask = finished[i];
                remove(finished, &i);
                break;
            }
        }
    }
    MOZ_ASSERT(parseTask);

    if (!maybecx) {
        LeaveParseTaskZone(rt, parseTask);
        return nullptr;
    }

    JSContext *cx = maybecx;
    MOZ_ASSERT(cx->compartment());

    // Make sure we have all the constructors we need for the prototype
    // remapping below, since we can't GC while that's happening.
    Rooted<GlobalObject*> global(cx, &cx->global()->as<GlobalObject>());
    if (!EnsureConstructor(cx, global, JSProto_Object) ||
        !EnsureConstructor(cx, global, JSProto_Array) ||
        !EnsureConstructor(cx, global, JSProto_Function) ||
        !EnsureConstructor(cx, global, JSProto_RegExp) ||
        !EnsureConstructor(cx, global, JSProto_Iterator))
    {
        LeaveParseTaskZone(rt, parseTask);
        return nullptr;
    }

    LeaveParseTaskZone(rt, parseTask);

    // Point the prototypes of any objects in the script's compartment to refer
    // to the corresponding prototype in the new compartment. This will briefly
    // create cross compartment pointers, which will be fixed by the
    // MergeCompartments call below.
    for (gc::ZoneCellIter iter(parseTask->cx->zone(), gc::FINALIZE_OBJECT_GROUP);
         !iter.done();
         iter.next())
    {
        ObjectGroup *group = iter.get<ObjectGroup>();
        TaggedProto proto(group->proto());
        if (!proto.isObject())
            continue;

        JSProtoKey key = JS::IdentifyStandardPrototype(proto.toObject());
        if (key == JSProto_Null)
            continue;
        MOZ_ASSERT(key == JSProto_Object || key == JSProto_Array ||
                   key == JSProto_Function || key == JSProto_RegExp ||
                   key == JSProto_Iterator);

        JSObject *newProto = GetBuiltinPrototypePure(global, key);
        MOZ_ASSERT(newProto);

        group->setProtoUnchecked(TaggedProto(newProto));
    }

    // Move the parsed script and all its contents into the desired compartment.
    gc::MergeCompartments(parseTask->cx->compartment(), cx->compartment());
    if (!parseTask->finish(cx))
        return nullptr;

    RootedScript script(rt, parseTask->script);
    assertSameCompartment(cx, script);

    // Report any error or warnings generated during the parse, and inform the
    // debugger about the compiled scripts.
    for (size_t i = 0; i < parseTask->errors.length(); i++)
        parseTask->errors[i]->throwError(cx);
    if (parseTask->overRecursed)
        js_ReportOverRecursed(cx);
    if (cx->isExceptionPending())
        return nullptr;

    if (script) {
        // The Debugger only needs to be told about the topmost script that was compiled.
        Debugger::onNewScript(cx, script);

        // Update the compressed source table with the result. This is normally
        // called by setCompressedSource when compilation occurs on the main thread.
        if (script->scriptSource()->hasCompressedSource())
            script->scriptSource()->updateCompressedSourceSet(rt);
    }

    return script;
}