Bug 1167409 - 5/5 - Initialize ScriptSourceObject even when off-main-thread compilation fails. r=jandem
☠☠ backed out by 3d882ef61333 ☠ ☠
authorKannan Vijayan <kvijayan@mozilla.com>
Tue, 28 Jul 2015 17:03:57 -0400
changeset 286719 08653d62e6c066e1b627336d856328473d056d99
parent 286718 211b839cfcdacf17c51edfbcaf842264868ff7f0
child 286720 6934c180598434cc0a07841aefb658cdfaec3499
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1167409
milestone42.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1167409 - 5/5 - Initialize ScriptSourceObject even when off-main-thread compilation fails. r=jandem
js/src/frontend/BytecodeCompiler.cpp
js/src/frontend/BytecodeCompiler.h
js/src/vm/HelperThreads.cpp
js/src/vm/HelperThreads.h
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -56,16 +56,18 @@ class MOZ_STACK_CLASS BytecodeCompiler
     void setEnclosingStaticScope(Handle<ScopeObject*> scope);
     void setSourceArgumentsNotIncluded();
 
     JSScript* compileScript(HandleObject scopeChain, HandleScript evalCaller,
                             unsigned staticLevel);
     bool compileFunctionBody(MutableHandleFunction fun, const AutoNameVector& formals,
                              GeneratorKind generatorKind);
 
+    ScriptSourceObject* sourceObjectPtr() const;
+
   private:
     bool checkLength();
     bool createScriptSource();
     bool maybeCompressSource();
     bool canLazilyParse();
     bool createParser();
     bool createSourceAndParser();
     bool createScript(bool savedCallerFun = false, unsigned staticLevel = 0);
@@ -113,17 +115,17 @@ class MOZ_STACK_CLASS BytecodeCompiler
     Maybe<Parser<SyntaxParseHandler>> syntaxParser;
     Maybe<Parser<FullParseHandler>> parser;
 
     Directives directives;
     TokenStream::Position startPosition;
 
     RootedScript script;
     Maybe<BytecodeEmitter> emitter;
-    };
+};
 
 AutoCompilationTraceLogger::AutoCompilationTraceLogger(ExclusiveContext* cx, const TraceLoggerTextId id)
   : logger(cx->isJSContext() ? TraceLoggerForMainThread(cx->asJSContext()->runtime())
                              : TraceLoggerForCurrentThread()),
     event(logger, TraceLogger_AnnotateScripts),
     scriptLogger(logger, event),
     typeLogger(logger, id)
 {}
@@ -564,17 +566,17 @@ BytecodeCompiler::markFunctionsWithinEva
 bool
 BytecodeCompiler::maybeCompleteCompressSource()
 {
     return !maybeSourceCompressor || maybeSourceCompressor->complete();
 }
 
 JSScript*
 BytecodeCompiler::compileScript(HandleObject scopeChain, HandleScript evalCaller,
-                                          unsigned staticLevel)
+                                unsigned staticLevel)
 {
     if (!createSourceAndParser())
         return nullptr;
 
     bool savedCallerFun = evalCaller && evalCaller->functionOrCallerFunction();
     if (!createScript(savedCallerFun, staticLevel))
         return nullptr;
 
@@ -709,16 +711,22 @@ BytecodeCompiler::compileFunctionBody(Mu
 
     if (!maybeCompleteCompressSource())
         return false;
 
     return true;
 }
 
 ScriptSourceObject*
+BytecodeCompiler::sourceObjectPtr() const
+{
+    return sourceObject.get();
+}
+
+ScriptSourceObject*
 frontend::CreateScriptSourceObject(ExclusiveContext* cx, const ReadOnlyCompileOptions& options)
 {
     ScriptSource* ss = cx->new_<ScriptSource>();
     if (!ss)
         return nullptr;
     ScriptSourceHolder ssHolder(ss);
 
     if (!ss->initFromOptions(cx, options))
@@ -748,34 +756,57 @@ frontend::CreateScriptSourceObject(Exclu
 JSScript*
 frontend::CompileScript(ExclusiveContext* cx, LifoAlloc* alloc, HandleObject scopeChain,
                         Handle<ScopeObject*> enclosingStaticScope,
                         HandleScript evalCaller,
                         const ReadOnlyCompileOptions& options,
                         SourceBufferHolder& srcBuf,
                         JSString* source_ /* = nullptr */,
                         unsigned staticLevel /* = 0 */,
-                        SourceCompressionTask* extraSct /* = nullptr */)
+                        SourceCompressionTask* extraSct /* = nullptr */,
+                        ScriptSourceObject** sourceObjectOut /* = nullptr */)
 {
     MOZ_ASSERT(srcBuf.get());
 
     /*
      * The scripted callerFrame can only be given for compile-and-go scripts
      * and non-zero static level requires callerFrame.
      */
     MOZ_ASSERT_IF(evalCaller, options.isRunOnce);
     MOZ_ASSERT_IF(evalCaller, options.forEval);
     MOZ_ASSERT_IF(evalCaller && evalCaller->strict(), options.strictOption);
     MOZ_ASSERT_IF(staticLevel != 0, evalCaller);
     MOZ_ASSERT_IF(staticLevel != 0, !options.sourceIsLazy);
 
+    MOZ_ASSERT_IF(sourceObjectOut, *sourceObjectOut == nullptr);
+
     BytecodeCompiler compiler(cx, alloc, options, srcBuf, TraceLogger_ParserCompileScript);
     compiler.maybeSetSourceCompressor(extraSct);
     compiler.setEnclosingStaticScope(enclosingStaticScope);
-    return compiler.compileScript(scopeChain, evalCaller, staticLevel);
+    JSScript* script = compiler.compileScript(scopeChain, evalCaller, staticLevel);
+
+    // frontend::CompileScript independently returns the
+    // ScriptSourceObject (SSO) for the compile.  This is used by
+    // off-main-thread script compilation (OMT-SC).
+    //
+    // OMT-SC cannot initialize the SSO when it is first constructed
+    // because the SSO is allocated initially in a separate compartment.
+    //
+    // After OMT-SC, the separate compartment is merged with the main
+    // compartment, at which point the JSScripts created become observable
+    // by the debugger via memory-space scanning.
+    //
+    // Whatever happens to the top-level script compilation (even if it
+    // fails and returns null), we must finish initializing the SSO.  This
+    // is because there may be valid inner scripts observable by the debugger
+    // which reference the partially-initialized SSO.
+    if (sourceObjectOut)
+        *sourceObjectOut = compiler.sourceObjectPtr();
+
+    return script;
 }
 
 bool
 frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const char16_t* chars, size_t length)
 {
     MOZ_ASSERT(cx->compartment() == lazy->functionNonDelazifying()->compartment());
 
     CompileOptions options(cx, lazy->version());
--- a/js/src/frontend/BytecodeCompiler.h
+++ b/js/src/frontend/BytecodeCompiler.h
@@ -22,17 +22,18 @@ struct SourceCompressionTask;
 
 namespace frontend {
 
 JSScript*
 CompileScript(ExclusiveContext* cx, LifoAlloc* alloc,
               HandleObject scopeChain, Handle<ScopeObject*> enclosingStaticScope,
               HandleScript evalCaller, const ReadOnlyCompileOptions& options,
               SourceBufferHolder& srcBuf, JSString* source_ = nullptr,
-              unsigned staticLevel = 0, SourceCompressionTask* extraSct = nullptr);
+              unsigned staticLevel = 0, SourceCompressionTask* extraSct = nullptr,
+              ScriptSourceObject** sourceObjectOut = nullptr);
 
 bool
 CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const char16_t* chars, size_t length);
 
 /*
  * enclosingStaticScope is a static enclosing scope (e.g. a StaticWithObject).
  * Must be null if the enclosing scope is a global.
  */
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -198,17 +198,17 @@ static const JSClass parseTaskGlobalClas
 
 ParseTask::ParseTask(ExclusiveContext* cx, JSObject* exclusiveContextGlobal, JSContext* initCx,
                      const char16_t* 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),
     exclusiveContextGlobal(initCx->runtime(), exclusiveContextGlobal),
     callback(callback), callbackData(callbackData),
-    script(nullptr), errors(cx), overRecursed(false)
+    script(nullptr), sourceObject(nullptr), errors(cx), overRecursed(false)
 {
 }
 
 bool
 ParseTask::init(JSContext* cx, const ReadOnlyCompileOptions& options)
 {
     if (!this->options.copy(cx, options))
         return false;
@@ -228,20 +228,18 @@ ParseTask::activate(JSRuntime* rt)
 {
     rt->setUsedByExclusiveThread(exclusiveContextGlobal->zone());
     cx->enterCompartment(exclusiveContextGlobal->compartment());
 }
 
 bool
 ParseTask::finish(JSContext* cx)
 {
-    if (script) {
-        // Finish off the ScriptSourceObject initialization that we put off in
-        // js::frontend::CreateScriptSourceObject.
-        RootedScriptSource sso(cx, &script->sourceObject()->as<ScriptSourceObject>());
+    if (sourceObject) {
+        RootedScriptSource sso(cx, sourceObject);
         if (!ScriptSourceObject::initFromOptions(cx, sso, options))
             return false;
     }
 
     return true;
 }
 
 ParseTask::~ParseTask()
@@ -1242,17 +1240,21 @@ HelperThread::handleParseWorkload()
         AutoUnlockHelperThreadState unlock;
         PerThreadData::AutoEnterRuntime enter(threadData.ptr(),
                                               parseTask->exclusiveContextGlobal->runtimeFromAnyThread());
         SourceBufferHolder srcBuf(parseTask->chars, parseTask->length,
                                   SourceBufferHolder::NoOwnership);
         parseTask->script = frontend::CompileScript(parseTask->cx, &parseTask->alloc,
                                                     nullptr, nullptr, nullptr,
                                                     parseTask->options,
-                                                    srcBuf);
+                                                    srcBuf,
+                                                    /* source_ = */ nullptr,
+                                                    /* staticLevel = */ 0,
+                                                    /* extraSct = */ nullptr,
+                                                    /* sourceObjectOut = */ &(parseTask->sourceObject));
     }
 
     // The callback is invoked while we are still off the main thread.
     parseTask->callback(parseTask, parseTask->callbackData);
 
     // FinishOffThreadScript will need to be called on the script to
     // migrate it into the correct compartment.
     HelperThreadState().parseFinishedList().append(parseTask);
--- a/js/src/vm/HelperThreads.h
+++ b/js/src/vm/HelperThreads.h
@@ -475,16 +475,19 @@ struct ParseTask
     JS::OffThreadCompileCallback callback;
     void* callbackData;
 
     // Holds the final script between the invocation of the callback and the
     // point where FinishOffThreadScript is called, which will destroy the
     // ParseTask.
     JSScript* script;
 
+    // Holds the ScriptSourceObject generated for the script compilation.
+    ScriptSourceObject* sourceObject;
+
     // Any errors or warnings produced during compilation. These are reported
     // when finishing the script.
     Vector<frontend::CompileError*> errors;
     bool overRecursed;
 
     ParseTask(ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
               JSContext* initCx, const char16_t* chars, size_t length,
               JS::OffThreadCompileCallback callback, void* callbackData);