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); }
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; }