diff --git a/content/base/src/nsScriptLoader.cpp b/content/base/src/nsScriptLoader.cpp index 33415dd23c490937ac280fd4e05a1a3b878a05c9..e51a3eaa643f7c9427aa4a6254891884970bdf0d 100644 --- a/content/base/src/nsScriptLoader.cpp +++ b/content/base/src/nsScriptLoader.cpp @@ -853,7 +853,7 @@ nsScriptLoader::AttemptAsyncScriptParse(nsScriptLoadRequest* aRequest) // This reference will be consumed by OffThreadScriptLoaderCallback. NS_ADDREF(runnable); - if (!JS::CompileOffThread(cx, global, options, + if (!JS::CompileOffThread(cx, options, aRequest->mScriptText.get(), aRequest->mScriptText.Length(), OffThreadScriptLoaderCallback, static_cast<void*>(runnable))) { diff --git a/content/xul/content/src/nsXULElement.cpp b/content/xul/content/src/nsXULElement.cpp index 9fd1de91d4e76e4c8f2fc7b559a291e05abc1857..05d50ecea921cd1efbd8de423b9e74b9372a0751 100644 --- a/content/xul/content/src/nsXULElement.cpp +++ b/content/xul/content/src/nsXULElement.cpp @@ -2662,7 +2662,7 @@ nsXULPrototypeScript::Compile(const char16_t* aText, } if (aOffThreadReceiver && JS::CanCompileOffThread(cx, options, aTextLength)) { - if (!JS::CompileOffThread(cx, scope, options, + if (!JS::CompileOffThread(cx, options, static_cast<const jschar*>(aText), aTextLength, OffThreadScriptReceiverCallback, static_cast<void*>(aOffThreadReceiver))) { diff --git a/js/src/jit-test/tests/debug/onNewScript-off-main-thread.js b/js/src/jit-test/tests/debug/onNewScript-off-main-thread.js index 08267f85230799cc99673a15c37ac7eaeff00995..b59307bb8efaf9bcf85ebcdc0e0e27ab85fc53b8 100644 --- a/js/src/jit-test/tests/debug/onNewScript-off-main-thread.js +++ b/js/src/jit-test/tests/debug/onNewScript-off-main-thread.js @@ -14,5 +14,5 @@ dbg.onNewScript = function (s) { log = ''; g.offThreadCompileScript('"t" + "wine"'); -assertEq(runOffThreadScript(), 'twine'); +assertEq(g.runOffThreadScript(), 'twine'); assertEq(log, 's'); diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 23ed775eac281460970096fc41f8b9e14ba45d4b..a74f809559c0c714164e441e0c571df56cc8c2f5 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -4519,12 +4519,12 @@ JS::CanCompileOffThread(JSContext *cx, const ReadOnlyCompileOptions &options, si } JS_PUBLIC_API(bool) -JS::CompileOffThread(JSContext *cx, Handle<JSObject*> obj, const ReadOnlyCompileOptions &options, +JS::CompileOffThread(JSContext *cx, const ReadOnlyCompileOptions &options, const jschar *chars, size_t length, OffThreadCompileCallback callback, void *callbackData) { JS_ASSERT(CanCompileOffThread(cx, options, length)); - return StartOffThreadParseScript(cx, options, chars, length, obj, callback, callbackData); + return StartOffThreadParseScript(cx, options, chars, length, callback, callbackData); } JS_PUBLIC_API(JSScript *) diff --git a/js/src/jsapi.h b/js/src/jsapi.h index d77fdf0d7116acecfbd5f115dc6f863160c95a84..5c48ebcb75d74ed88e4950975bfde2540d538d15 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -3641,8 +3641,8 @@ CanCompileOffThread(JSContext *cx, const ReadOnlyCompileOptions &options, size_t * for the compilation. The callback will be invoked while off the main thread, * so must ensure that its operations are thread safe. Afterwards, * FinishOffThreadScript must be invoked on the main thread to get the result - * script or nullptr. If maybecx is specified, this method will also report - * any error or warnings generated during the parse. + * script or nullptr. If maybecx is not specified, the resources will be freed, + * but no script will be returned. * * The characters passed in to CompileOffThread must remain live until the * callback is invoked, and the resulting script will be rooted until the call @@ -3650,7 +3650,7 @@ CanCompileOffThread(JSContext *cx, const ReadOnlyCompileOptions &options, size_t */ extern JS_PUBLIC_API(bool) -CompileOffThread(JSContext *cx, HandleObject obj, const ReadOnlyCompileOptions &options, +CompileOffThread(JSContext *cx, const ReadOnlyCompileOptions &options, const jschar *chars, size_t length, OffThreadCompileCallback callback, void *callbackData); diff --git a/js/src/jsworkers.cpp b/js/src/jsworkers.cpp index e40639852b0e6e5b54b25a22c33fcc0cfcbb4fb5..b5e42357ee93b325f7130d5bcbbff4ced893f693 100644 --- a/js/src/jsworkers.cpp +++ b/js/src/jsworkers.cpp @@ -176,10 +176,10 @@ static const JSClass workerGlobalClass = { }; ParseTask::ParseTask(ExclusiveContext *cx, JSObject *exclusiveContextGlobal, JSContext *initCx, - const jschar *chars, size_t length, JSObject *scopeChain, + const jschar *chars, size_t length, JS::OffThreadCompileCallback callback, void *callbackData) : cx(cx), options(initCx), chars(chars), length(length), - alloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), scopeChain(initCx, scopeChain), + alloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), exclusiveContextGlobal(initCx, exclusiveContextGlobal), optionsElement(initCx), optionsIntroductionScript(initCx), callback(callback), callbackData(callbackData), script(nullptr), errors(cx), overRecursed(false) @@ -293,7 +293,7 @@ js::OffThreadParsingMustWaitForGC(JSRuntime *rt) bool js::StartOffThreadParseScript(JSContext *cx, const ReadOnlyCompileOptions &options, - const jschar *chars, size_t length, HandleObject scopeChain, + const jschar *chars, size_t length, JS::OffThreadCompileCallback callback, void *callbackData) { // Suppress GC so that calls below do not trigger a new incremental GC @@ -351,7 +351,7 @@ js::StartOffThreadParseScript(JSContext *cx, const ReadOnlyCompileOptions &optio ScopedJSDeletePtr<ParseTask> task( cx->new_<ParseTask>(workercx.get(), global, cx, chars, length, - scopeChain, callback, callbackData)); + callback, callbackData)); if (!task) return false; @@ -615,7 +615,7 @@ CallNewScriptHookForAllScripts(JSContext *cx, HandleScript script) JSScript * GlobalWorkerThreadState::finishParseTask(JSContext *maybecx, JSRuntime *rt, void *token) { - ParseTask *parseTask = nullptr; + ScopedJSDeletePtr<ParseTask> parseTask; // The token is a ParseTask* which should be in the finished list. // Find and remove its entry. @@ -635,6 +635,23 @@ GlobalWorkerThreadState::finishParseTask(JSContext *maybecx, JSRuntime *rt, void // Mark the zone as no longer in use by an ExclusiveContext, and available // to be collected by the GC. rt->clearUsedByExclusiveThread(parseTask->cx->zone()); + if (!maybecx) { + return nullptr; + } + JSContext *cx = maybecx; + JS_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 (!GlobalObject::ensureConstructor(cx, global, JSProto_Object) || + !GlobalObject::ensureConstructor(cx, global, JSProto_Array) || + !GlobalObject::ensureConstructor(cx, global, JSProto_Function) || + !GlobalObject::ensureConstructor(cx, global, JSProto_RegExp) || + !GlobalObject::ensureConstructor(cx, global, JSProto_Iterator)) + { + return nullptr; + } // Point the prototypes of any objects in the script's compartment to refer // to the corresponding prototype in the new compartment. This will briefly @@ -652,41 +669,41 @@ GlobalWorkerThreadState::finishParseTask(JSContext *maybecx, JSRuntime *rt, void JSProtoKey key = JS::IdentifyStandardPrototype(proto.toObject()); if (key == JSProto_Null) continue; + JS_ASSERT(key == JSProto_Object || key == JSProto_Array || + key == JSProto_Function || key == JSProto_RegExp || + key == JSProto_Iterator); - JSObject *newProto = GetBuiltinPrototypePure(&parseTask->scopeChain->global(), key); + JSObject *newProto = GetBuiltinPrototypePure(global, key); JS_ASSERT(newProto); object->setProtoUnchecked(newProto); } // Move the parsed script and all its contents into the desired compartment. - gc::MergeCompartments(parseTask->cx->compartment(), parseTask->scopeChain->compartment()); + gc::MergeCompartments(parseTask->cx->compartment(), cx->compartment()); parseTask->finish(); RootedScript script(rt, parseTask->script); + assertSameCompartment(cx, script); - // If we have a context, report any error or warnings generated during the - // parse, and inform the debugger about the compiled scripts. - if (maybecx) { - AutoCompartment ac(maybecx, parseTask->scopeChain); - for (size_t i = 0; i < parseTask->errors.length(); i++) - parseTask->errors[i]->throwError(maybecx); - if (parseTask->overRecursed) - js_ReportOverRecursed(maybecx); - - if (script) { - // The Debugger only needs to be told about the topmost script that was compiled. - GlobalObject *compileAndGoGlobal = nullptr; - if (script->compileAndGo()) - compileAndGoGlobal = &script->global(); - Debugger::onNewScript(maybecx, script, compileAndGoGlobal); - - // The NewScript hook needs to be called for all compiled scripts. - CallNewScriptHookForAllScripts(maybecx, 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 (script) { + // The Debugger only needs to be told about the topmost script that was compiled. + GlobalObject *compileAndGoGlobal = nullptr; + if (script->compileAndGo()) + compileAndGoGlobal = &script->global(); + Debugger::onNewScript(cx, script, compileAndGoGlobal); + + // The NewScript hook needs to be called for all compiled scripts. + CallNewScriptHookForAllScripts(cx, script); } - js_delete(parseTask); return script; } @@ -1071,7 +1088,7 @@ js::CancelOffThreadParses(JSRuntime *rt) bool js::StartOffThreadParseScript(JSContext *cx, const ReadOnlyCompileOptions &options, - const jschar *chars, size_t length, HandleObject scopeChain, + const jschar *chars, size_t length, JS::OffThreadCompileCallback callback, void *callbackData) { MOZ_ASSUME_UNREACHABLE("Off thread compilation not available in non-THREADSAFE builds"); diff --git a/js/src/jsworkers.h b/js/src/jsworkers.h index e33158743f2a3d7a2d84cd5d40ffb38f31f89ac7..74b6f1e7ca149f5f2f02fed86026b8164ba1b285 100644 --- a/js/src/jsworkers.h +++ b/js/src/jsworkers.h @@ -300,7 +300,7 @@ CancelOffThreadParses(JSRuntime *runtime); */ bool StartOffThreadParseScript(JSContext *cx, const ReadOnlyCompileOptions &options, - const jschar *chars, size_t length, HandleObject scopeChain, + const jschar *chars, size_t length, JS::OffThreadCompileCallback callback, void *callbackData); /* @@ -391,11 +391,6 @@ struct ParseTask size_t length; LifoAlloc alloc; - // Rooted pointer to the scope in the target compartment which the - // resulting script will be merged into. This is not safe to use off the - // main thread. - PersistentRootedObject scopeChain; - // Rooted pointer to the global object used by 'cx'. PersistentRootedObject exclusiveContextGlobal; @@ -421,8 +416,8 @@ struct ParseTask Vector<frontend::CompileError *> errors; bool overRecursed; - ParseTask(ExclusiveContext *cx, JSObject *exclusiveContextGlobal, JSContext *initCx, - const jschar *chars, size_t length, JSObject *scopeChain, + ParseTask(ExclusiveContext *cx, JSObject *exclusiveContextGlobal, + JSContext *initCx, const jschar *chars, size_t length, JS::OffThreadCompileCallback callback, void *callbackData); bool init(JSContext *cx, const ReadOnlyCompileOptions &options); diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 0f764e9ec1af4fb1bb310455520a6f98d57d7933..c091948a318c1d4d6eb38c761bbfc0a911eee157 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -3665,7 +3665,7 @@ OffThreadCompileScript(JSContext *cx, unsigned argc, jsval *vp) return false; } - if (!JS::CompileOffThread(cx, cx->global(), options, chars, length, + if (!JS::CompileOffThread(cx, options, chars, length, OffThreadCompileScriptCallback, nullptr)) { offThreadState.abandon(cx);