Bug 1316081 part 2 - Add an XDRIncrementalEncoder instance on the ScriptSource. r=shu
authorNicolas B. Pierron <nicolas.b.pierron@mozilla.com>
Tue, 31 Jan 2017 20:03:57 +0000
changeset 331936 1dfb4475f41e13db37e82be5a0aa6e5fd5f26510
parent 331935 05ab647f6e78724a23a702b62e71743d27420533
child 331937 c261e950629ff63ad48a05fe4a399e5811df0f6f
push id36738
push usercbook@mozilla.com
push dateWed, 01 Feb 2017 12:27:08 +0000
treeherderautoland@e9f38cda7664 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersshu
bugs1316081
milestone54.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 1316081 part 2 - Add an XDRIncrementalEncoder instance on the ScriptSource. r=shu
js/src/frontend/BytecodeCompiler.cpp
js/src/jsscript.cpp
js/src/jsscript.h
js/src/vm/Xdr.cpp
js/src/vm/Xdr.h
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -666,16 +666,23 @@ frontend::CompileLazyFunction(JSContext*
         return false;
 
     if (!bce.emitFunctionScript(pn->pn_body))
         return false;
 
     if (!NameFunctions(cx, pn))
         return false;
 
+    // XDR the newly delazified function.
+    if (script->scriptSource()->hasEncoder() &&
+        !script->scriptSource()->xdrEncodeFunction(cx, fun, sourceObject))
+    {
+        return false;
+    }
+
     return true;
 }
 
 bool
 frontend::CompileStandaloneFunction(JSContext* cx, MutableHandleFunction fun,
                                     const ReadOnlyCompileOptions& options,
                                     JS::SourceBufferHolder& srcBuf,
                                     Maybe<uint32_t> parameterListEnd,
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1907,16 +1907,74 @@ ScriptSource::addSizeOfIncludingThis(moz
                                      JS::ScriptSourceInfo* info) const
 {
     info->misc += mallocSizeOf(this) +
                   mallocSizeOf(filename_.get()) +
                   mallocSizeOf(introducerFilename_.get());
     info->numScripts++;
 }
 
+bool
+ScriptSource::xdrEncodeTopLevel(ExclusiveContext* cx, JS::TranscodeBuffer& buffer,
+                                HandleScript script)
+{
+    xdrEncoder_ = js::MakeUnique<XDRIncrementalEncoder>(cx, buffer, buffer.length());
+    if (!xdrEncoder_) {
+        ReportOutOfMemory(cx);
+        return false;
+    }
+
+    MOZ_ASSERT(hasEncoder());
+    auto failureCase = mozilla::MakeScopeExit([&] {
+        xdrEncoder_.reset(nullptr);
+    });
+
+    if (!xdrEncoder_->init()) {
+        ReportOutOfMemory(cx);
+        return false;
+    }
+
+    RootedScript s(cx, script);
+    if (!xdrEncoder_->codeScript(&s))
+        return false;
+
+    failureCase.release();
+    return true;
+}
+
+bool
+ScriptSource::xdrEncodeFunction(ExclusiveContext* cx, HandleFunction fun, HandleScriptSource sourceObject)
+{
+    MOZ_ASSERT(sourceObject->source() == this);
+    MOZ_ASSERT(hasEncoder());
+    auto failureCase = mozilla::MakeScopeExit([&] {
+        xdrEncoder_.reset(nullptr);
+    });
+
+    RootedFunction f(cx, fun);
+    if (!xdrEncoder_->codeFunction(&f, sourceObject))
+        return false;
+
+    failureCase.release();
+    return true;
+}
+
+bool
+ScriptSource::xdrFinalizeEncoder()
+{
+    MOZ_ASSERT(hasEncoder());
+    auto cleanup = mozilla::MakeScopeExit([&] {
+        xdrEncoder_.reset(nullptr);
+    });
+
+    if (!xdrEncoder_->linearize())
+        return false;
+    return true;
+}
+
 template<XDRMode mode>
 bool
 ScriptSource::performXDR(XDRState<mode>* xdr)
 {
     struct CompressedLengthMatcher
     {
         size_t match(Uncompressed&) {
             return 0;
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -427,16 +427,21 @@ class ScriptSource
     //      "importScripts" for code by calling |importScripts| in a web worker.
     //      "handler" for code assigned to DOM elements' event handler IDL attributes.
     //      "scriptElement" for code belonging to <script> elements.
     //      undefined if the implementation doesn't know how the code was introduced.
     // This is a constant, statically allocated C string, so does not need
     // memory management.
     const char* introductionType_;
 
+    // The bytecode cache encoder is used to encode only the content of function
+    // which are delazified.  If this value is not nullptr, then each delazified
+    // function should be recorded before their first execution.
+    UniquePtr<XDRIncrementalEncoder> xdrEncoder_;
+
     // True if we can call JSRuntime::sourceHook to load the source on
     // demand. If sourceRetrievable_ and hasSourceData() are false, it is not
     // possible to get source at all.
     bool sourceRetrievable_:1;
     bool hasIntroductionOffset_:1;
 
     const char16_t* chunkChars(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& holder,
                                size_t chunk);
@@ -448,16 +453,17 @@ class ScriptSource
         filename_(nullptr),
         displayURL_(nullptr),
         sourceMapURL_(nullptr),
         mutedErrors_(false),
         introductionOffset_(0),
         parameterListEnd_(0),
         introducerFilename_(nullptr),
         introductionType_(nullptr),
+        xdrEncoder_(nullptr),
         sourceRetrievable_(false),
         hasIntroductionOffset_(false)
     {
     }
 
     ~ScriptSource() {
         MOZ_ASSERT(refs == 0);
     }
@@ -574,16 +580,39 @@ class ScriptSource
         MOZ_ASSERT(offset <= (uint32_t)INT32_MAX);
         introductionOffset_ = offset;
         hasIntroductionOffset_ = true;
     }
 
     uint32_t parameterListEnd() const {
         return parameterListEnd_;
     }
+
+    // Return wether an XDR encoder is present or not.
+    bool hasEncoder() const { return bool(xdrEncoder_); }
+
+    // Create a new XDR encoder, and encode the top-level JSScript. The result
+    // of the encoding would be available in the |buffer| provided as argument,
+    // as soon as |xdrFinalize| is called and all xdr function calls returned
+    // successfully.
+    bool xdrEncodeTopLevel(ExclusiveContext* cx, JS::TranscodeBuffer& buffer, HandleScript script);
+
+    // Encode a delazified JSFunction.  In case of errors, the XDR encoder is
+    // freed and the |buffer| provided as argument to |xdrEncodeTopLevel| is
+    // considered undefined.
+    //
+    // The |sourceObject| argument is the object holding the current
+    // ScriptSource.
+    bool xdrEncodeFunction(ExclusiveContext* cx, HandleFunction fun,
+                           HandleScriptSource sourceObject);
+
+    // Linearize the encoded content in the |buffer| provided as argument to
+    // |xdrEncodeTopLevel|, and free the XDR encoder.  In case of errors, the
+    // |buffer| is considered undefined.
+    bool xdrFinalizeEncoder();
 };
 
 class ScriptSourceHolder
 {
     ScriptSource* ss;
   public:
     ScriptSourceHolder()
       : ss(nullptr)
--- a/js/src/vm/Xdr.cpp
+++ b/js/src/vm/Xdr.cpp
@@ -114,41 +114,45 @@ VersionCheck(XDRState<mode>* xdr)
             return xdr->fail(JS::TranscodeResult_Failure_BadBuildId);
     }
 
     return true;
 }
 
 template<XDRMode mode>
 bool
-XDRState<mode>::codeFunction(MutableHandleFunction funp)
+XDRState<mode>::codeFunction(MutableHandleFunction funp, HandleScriptSource sourceObject)
 {
     TraceLoggerThread* logger = nullptr;
     if (cx()->isJSContext())
         logger = TraceLoggerForMainThread(cx()->asJSContext()->runtime());
     else
         logger = TraceLoggerForCurrentThread();
     TraceLoggerTextId event =
         mode == XDR_DECODE ? TraceLogger_DecodeFunction : TraceLogger_EncodeFunction;
     AutoTraceLog tl(logger, event);
 
     RootedScope scope(cx(), &cx()->global()->emptyGlobalScope());
-    if (mode == XDR_DECODE)
+    if (mode == XDR_DECODE) {
+        MOZ_ASSERT(!sourceObject);
         funp.set(nullptr);
-    else if (getTreeKey(funp) != AutoXDRTree::noKey)
+    } else if (getTreeKey(funp) != AutoXDRTree::noKey) {
+        MOZ_ASSERT(sourceObject);
         scope = funp->nonLazyScript()->enclosingScope();
-    else
+    } else {
+        MOZ_ASSERT(!sourceObject);
         MOZ_ASSERT(funp->nonLazyScript()->enclosingScope()->is<GlobalScope>());
+    }
 
     if (!VersionCheck(this)) {
         postProcessContextErrors(cx());
         return false;
     }
 
-    if (!XDRInterpretedFunction(this, scope, nullptr, funp)) {
+    if (!XDRInterpretedFunction(this, scope, sourceObject, funp)) {
         postProcessContextErrors(cx());
         funp.set(nullptr);
         return false;
     }
 
     return true;
 }
 
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -276,17 +276,17 @@ class XDRState : public XDRCoderBase
             *sp = buf.readCString();
         }
         return true;
     }
 
     bool codeChars(const JS::Latin1Char* chars, size_t nchars);
     bool codeChars(char16_t* chars, size_t nchars);
 
-    bool codeFunction(JS::MutableHandleFunction objp);
+    bool codeFunction(JS::MutableHandleFunction objp, HandleScriptSource sourceObject = nullptr);
     bool codeScript(MutableHandleScript scriptp);
     bool codeConstValue(MutableHandleValue vp);
 };
 
 using XDREncoder = XDRState<XDR_ENCODE>;
 using XDRDecoder = XDRState<XDR_DECODE>;
 
 class XDROffThreadDecoder : public XDRDecoder