Bug 1662274 - Support incremental XDR encoding of stencil. r=tcampbell

Differential Revision: https://phabricator.services.mozilla.com/D91436
This commit is contained in:
Tooru Fujisawa 2020-09-28 13:24:00 +00:00
Родитель 9e6d052207
Коммит 2119eb41da
7 изменённых файлов: 475 добавлений и 112 удалений

Просмотреть файл

@ -44,7 +44,7 @@
# include "frontend/TokenStream.h"
#endif
#include "frontend/BytecodeCompilation.h"
#include "frontend/CompilationInfo.h"
#include "frontend/CompilationInfo.h" // frontend::CompilationInfo, frontend::CompilationInfoVector
#include "gc/Allocator.h"
#include "gc/Zone.h"
#include "jit/BaselineJIT.h"
@ -4959,26 +4959,26 @@ static bool EvalStencilXDR(JSContext* cx, uint32_t argc, Value* vp) {
const char* filename = "compileStencilXDR-DATA.js";
uint32_t lineno = 1;
/* Prepare the CompilationInfo for decoding. */
/* Prepare the CompilationInfoVector for decoding. */
CompileOptions options(cx);
options.setFileAndLine(filename, lineno);
options.setForceFullParse();
Rooted<frontend::CompilationInfo> compilationInfo(
cx, frontend::CompilationInfo(cx, options));
if (!compilationInfo.get().input.initForGlobal(cx)) {
Rooted<frontend::CompilationInfoVector> compilationInfos(
cx, frontend::CompilationInfoVector(cx, options));
if (!compilationInfos.get().initial.input.initForGlobal(cx)) {
return false;
}
/* Deserialize the stencil from XDR. */
JS::TranscodeRange xdrRange(src->dataPointer(), src->byteLength());
if (!compilationInfo.get().deserializeStencils(cx, xdrRange)) {
if (!compilationInfos.get().deserializeStencils(cx, xdrRange)) {
return false;
}
/* Instantiate the stencil. */
frontend::CompilationGCOutput output(cx);
if (!compilationInfo.get().instantiateStencils(cx, output)) {
if (!compilationInfos.get().instantiateStencils(cx, output)) {
return false;
}

Просмотреть файл

@ -1156,3 +1156,8 @@ void CompilationInput::trace(JSTracer* trc) {
}
void CompilationInfo::trace(JSTracer* trc) { input.trace(trc); }
void CompilationInfoVector::trace(JSTracer* trc) {
initial.trace(trc);
delazifications.trace(trc);
}

Просмотреть файл

@ -386,9 +386,6 @@ struct CompilationInfo {
CompilationGCOutput& gcOutput);
MOZ_MUST_USE bool serializeStencils(JSContext* cx, JS::TranscodeBuffer& buf,
bool* succeededOut = nullptr);
MOZ_MUST_USE bool deserializeStencils(JSContext* cx,
const JS::TranscodeRange& range,
bool* succeededOut = nullptr);
JSAtom* liftParserAtomToJSAtom(JSContext* cx, const ParserAtom* parserAtom) {
return parserAtom->toJSAtom(cx, *this).unwrapOr(nullptr);
@ -413,6 +410,44 @@ struct CompilationInfo {
void trace(JSTracer* trc);
};
// A set of CompilationInfo, for XDR purpose.
// This contains the initial compilation, and a vector of delazification.
struct CompilationInfoVector {
private:
using FunctionKey = uint64_t;
using FunctionMap = HashMap<FunctionKey, FunctionIndex>;
static FunctionKey toFunctionKey(const SourceExtent& extent) {
return (FunctionKey)extent.sourceStart << 32 | extent.sourceEnd;
}
MOZ_MUST_USE bool buildDelazificationStencilMap(FunctionMap& functionMap);
public:
frontend::CompilationInfo initial;
GCVector<frontend::CompilationInfo, 0, js::SystemAllocPolicy> delazifications;
CompilationInfoVector(JSContext* cx,
const JS::ReadOnlyCompileOptions& options)
: initial(cx, options) {}
// Move constructor is necessary to use Rooted.
CompilationInfoVector(CompilationInfoVector&&) = default;
// To avoid any misuses, make sure this is neither copyable or assignable.
CompilationInfoVector(const CompilationInfoVector&) = delete;
CompilationInfoVector& operator=(const CompilationInfoVector&) = delete;
CompilationInfoVector& operator=(CompilationInfoVector&&) = delete;
MOZ_MUST_USE bool instantiateStencils(JSContext* cx,
CompilationGCOutput& gcOutput);
MOZ_MUST_USE bool deserializeStencils(JSContext* cx,
const JS::TranscodeRange& range,
bool* succeededOut = nullptr);
void trace(JSTracer* trc);
};
} // namespace frontend
} // namespace js
#endif

Просмотреть файл

@ -10,7 +10,7 @@
#include "frontend/AbstractScopePtr.h" // ScopeIndex
#include "frontend/BytecodeSection.h" // EmitScriptThingsVector
#include "frontend/CompilationInfo.h" // CompilationInfo
#include "frontend/CompilationInfo.h" // CompilationInfo, CompilationInfoVector, CompilationGCOutput
#include "frontend/SharedContext.h"
#include "gc/AllocKind.h" // gc::AllocKind
#include "js/CallArgs.h" // JSNative
@ -617,14 +617,68 @@ bool CompilationInfo::instantiateStencils(JSContext* cx,
return true;
}
bool CompilationInfoVector::buildDelazificationStencilMap(
FunctionMap& functionMap) {
// Stantdlone-functions are not supported by XDR.
MOZ_ASSERT(!initial.stencil.scriptData[0].isFunction());
if (!functionMap.reserve(initial.stencil.scriptData.length() - 1)) {
return false;
}
for (size_t i = 1; i < initial.stencil.scriptData.length(); i++) {
if (!functionMap.put(toFunctionKey(initial.stencil.scriptData[i].extent),
FunctionIndex(i))) {
return false;
}
}
return true;
}
bool CompilationInfoVector::instantiateStencils(JSContext* cx,
CompilationGCOutput& gcOutput) {
if (!initial.instantiateStencils(cx, gcOutput)) {
return false;
}
FunctionMap functionMap(cx);
if (!buildDelazificationStencilMap(functionMap)) {
return false;
}
for (auto& delazification : delazifications) {
auto p = functionMap.lookup(
toFunctionKey(delazification.stencil.scriptData[0].extent));
MOZ_ASSERT(p);
JSFunction* fun = gcOutput.functions[p->value()];
MOZ_ASSERT(fun);
BaseScript* lazy = fun->baseScript();
MOZ_ASSERT(!lazy->hasBytecode());
// CompilationInfo.input for delazification isn't initialized when
// decoding.
delazification.input.initFromLazy(lazy);
CompilationGCOutput gcOutputForDelazification(cx);
if (!delazification.instantiateStencils(cx, gcOutputForDelazification)) {
return false;
}
}
return true;
}
bool CompilationInfo::serializeStencils(JSContext* cx, JS::TranscodeBuffer& buf,
bool* succeededOut) {
if (succeededOut) {
*succeededOut = false;
}
XDRIncrementalStencilEncoder encoder(cx, *this);
XDRIncrementalStencilEncoder encoder(cx);
XDRResult res = encoder.codeStencil(stencil);
XDRResult res = encoder.codeStencil(*this);
if (res.isErr()) {
if (res.unwrapErr() & JS::TranscodeResult_Failure) {
buf.clear();
@ -649,17 +703,17 @@ bool CompilationInfo::serializeStencils(JSContext* cx, JS::TranscodeBuffer& buf,
return true;
}
bool CompilationInfo::deserializeStencils(JSContext* cx,
const JS::TranscodeRange& range,
bool* succeededOut) {
bool CompilationInfoVector::deserializeStencils(JSContext* cx,
const JS::TranscodeRange& range,
bool* succeededOut) {
if (succeededOut) {
*succeededOut = false;
}
MOZ_ASSERT(stencil.parserAtoms.empty());
XDRStencilDecoder decoder(cx, &input.options, range, *this,
stencil.parserAtoms);
MOZ_ASSERT(initial.stencil.parserAtoms.empty());
XDRStencilDecoder decoder(cx, &initial.input.options, range,
initial.stencil.parserAtoms);
XDRResult res = decoder.codeStencil(stencil);
XDRResult res = decoder.codeStencils(*this);
if (res.isErr()) {
if (res.unwrapErr() & JS::TranscodeResult_Failure) {
return true;

Просмотреть файл

@ -859,27 +859,47 @@ template <XDRMode mode>
namespace js {
template <XDRMode mode>
XDRResult XDRCompilationStencil(XDRState<mode>* xdr,
CompilationStencil& stencil) {
if (!stencil.asmJS.empty()) {
return xdr->fail(JS::TranscodeResult_Failure_AsmJSNotSupported);
}
XDRResult XDRCompilationInput(XDRState<mode>* xdr, CompilationInput& input) {
// XDR the ScriptSource
CompilationInfo& compilationInfo = xdr->stencilCompilationInfo();
// Instrumented scripts cannot be encoded, as they have extra instructions
// which are not normally present. Globals with instrumentation enabled must
// compile scripts via the bytecode emitter, which will insert these
// instructions.
if (mode == XDR_ENCODE) {
if (!!input.options.instrumentationKinds) {
return xdr->fail(JS::TranscodeResult_Failure);
}
}
// Copy the options out for passing into `ScriptSource::XDR`.
mozilla::Maybe<JS::CompileOptions> opts;
opts.emplace(xdr->cx(), compilationInfo.input.options);
opts.emplace(xdr->cx(), input.options);
Rooted<ScriptSourceHolder> holder(xdr->cx());
if (mode == XDR_ENCODE) {
holder.get().reset(compilationInfo.input.source_.get());
holder.get().reset(input.source_.get());
}
MOZ_TRY(ScriptSource::XDR(xdr, opts, &holder));
if (mode == XDR_DECODE) {
compilationInfo.input.source_.reset(holder.get().get());
input.source_.reset(holder.get().get());
}
return Ok();
}
template XDRResult XDRCompilationInput(XDRState<XDR_ENCODE>* xdr,
CompilationInput& input);
template XDRResult XDRCompilationInput(XDRState<XDR_DECODE>* xdr,
CompilationInput& input);
template <XDRMode mode>
XDRResult XDRCompilationStencil(XDRState<mode>* xdr,
CompilationStencil& stencil) {
if (!stencil.asmJS.empty()) {
return xdr->fail(JS::TranscodeResult_Failure_AsmJSNotSupported);
}
// All of the vector-indexed data elements referenced by the

Просмотреть файл

@ -19,8 +19,9 @@
#include "builtin/ModuleObject.h"
#include "debugger/DebugAPI.h"
#include "frontend/ParserAtom.h" // XDRParserAtom
#include "js/BuildId.h" // JS::BuildIdCharVector
#include "frontend/CompilationInfo.h" // frontend::CompilationStencil, frontend::CompilationInfo, frontend::CompilationInfoVector
#include "frontend/ParserAtom.h" // XDRParserAtom
#include "js/BuildId.h" // JS::BuildIdCharVector
#include "vm/JSContext.h"
#include "vm/JSScript.h"
#include "vm/TraceLogging.h"
@ -282,22 +283,11 @@ static XDRResult AtomTable(XDRState<mode>* xdr) {
MOZ_TRY(XDRAtomCount(xdr, &atomCount));
MOZ_ASSERT(!xdr->hasAtomTable());
if (xdr->isForStencil()) {
for (uint32_t i = 0; i < atomCount; i++) {
const frontend::ParserAtom* atom = nullptr;
MOZ_TRY(XDRParserAtom(xdr, &atom));
if (!xdr->parserAtomTable().append(atom)) {
ReportOutOfMemory(xdr->cx());
return xdr->fail(JS::TranscodeResult_Throw);
}
}
} else {
for (uint32_t i = 0; i < atomCount; i++) {
RootedAtom atom(xdr->cx());
MOZ_TRY(XDRAtom(xdr, &atom));
if (!xdr->atomTable().append(atom)) {
return xdr->fail(JS::TranscodeResult_Throw);
}
for (uint32_t i = 0; i < atomCount; i++) {
RootedAtom atom(xdr->cx());
MOZ_TRY(XDRAtom(xdr, &atom));
if (!xdr->atomTable().append(atom)) {
return xdr->fail(JS::TranscodeResult_Throw);
}
}
xdr->finishAtomTable();
@ -306,6 +296,39 @@ static XDRResult AtomTable(XDRState<mode>* xdr) {
return Ok();
}
template <XDRMode mode>
static XDRResult ParserAtomTable(XDRState<mode>* xdr) {
if (mode == XDR_ENCODE) {
return Ok();
}
// If we are incrementally encoding, the atom table will be built up over the
// course of the encoding. In XDRIncrementalStencilEncoder::finishChunk, we
// will write the number of atoms into the finished chunk, then append the
// completed atom table.
// If we are decoding, then we read the length and decode the atom table now.
uint32_t atomCount;
MOZ_TRY(XDRAtomCount(xdr, &atomCount));
MOZ_ASSERT(!xdr->hasAtomTable());
for (uint32_t i = 0; i < atomCount; i++) {
const frontend::ParserAtom* atom = nullptr;
MOZ_TRY(XDRParserAtom(xdr, &atom));
if (!xdr->parserAtomTable().append(atom)) {
ReportOutOfMemory(xdr->cx());
return xdr->fail(JS::TranscodeResult_Throw);
}
}
xdr->finishAtomTable();
return Ok();
}
template <XDRMode mode>
static XDRResult XDRChunkCount(XDRState<mode>* xdr, uint32_t* sliceCount) {
return xdr->codeUint32(sliceCount);
}
template <XDRMode mode>
XDRResult XDRState<mode>::codeFunction(MutableHandleFunction funp,
HandleScriptSourceObject sourceObject) {
@ -377,25 +400,68 @@ XDRResult XDRState<mode>::codeScript(MutableHandleScript scriptp) {
}
template <XDRMode mode>
XDRResult XDRState<mode>::codeStencil(frontend::CompilationStencil& stencil) {
XDRResult XDRState<mode>::codeStencil(
frontend::CompilationInfo& compilationInfo) {
#ifdef DEBUG
auto sanityCheck = mozilla::MakeScopeExit(
[&] { MOZ_ASSERT(validateResultCode(cx(), resultCode())); });
#endif
AutoXDRTree scriptTree(this, getTopLevelTreeKey());
// As with codeScript, use header buffer when incrementally encoding.
bool useHeader = this->hasAtomMap();
if (useHeader) {
if (mode == XDR_ENCODE) {
switchToHeaderBuf();
}
MOZ_TRY(VersionCheck(this));
MOZ_TRY(AtomTable(this));
if (useHeader) {
if (hasOptions()) {
MOZ_ASSERT(&options() == &compilationInfo.input.options);
}
MOZ_TRY(XDRCompilationInput(this, compilationInfo.input));
// If we are incrementally encoding, the number of chunks are encoded in
// XDRIncrementalStencilEncoder::linearize, after the header.
if (mode == XDR_DECODE) {
MOZ_TRY(XDRChunkCount(this, &nchunks()));
}
if (mode == XDR_ENCODE) {
switchToAtomBuf();
}
MOZ_TRY(ParserAtomTable(this));
if (mode == XDR_ENCODE) {
switchToMainBuf();
}
MOZ_ASSERT(isMainBuf());
MOZ_TRY(XDRCompilationStencil(this, compilationInfo.stencil));
MOZ_TRY(finishChunk());
return Ok();
}
template <XDRMode mode>
XDRResult XDRState<mode>::codeFunctionStencil(
frontend::CompilationStencil& stencil) {
#ifdef DEBUG
auto sanityCheck = mozilla::MakeScopeExit(
[&] { MOZ_ASSERT(validateResultCode(cx(), resultCode())); });
#endif
bool isAlreadyCoded = false;
MOZ_TRY_VAR(isAlreadyCoded, checkAlreadyCoded(stencil));
if (isAlreadyCoded) {
return Ok();
}
if (mode == XDR_ENCODE) {
switchToAtomBuf();
}
MOZ_TRY(ParserAtomTable(this));
if (mode == XDR_ENCODE) {
switchToMainBuf();
}
MOZ_TRY(XDRCompilationStencil(this, stencil));
MOZ_TRY(finishChunk());
return Ok();
}
@ -626,6 +692,105 @@ XDRResult XDRIncrementalEncoder::linearize(JS::TranscodeBuffer& buffer) {
return Ok();
}
XDRResult XDRIncrementalStencilEncoder::finishChunk() {
switchToFinishedChunkBuf();
MOZ_TRY(XDRAtomCount(this, &natoms_));
MOZ_TRY(codeBytes(atoms_.begin(), atoms_.length()));
MOZ_TRY(codeBytes(slices_.begin(), slices_.length()));
switchToMainBuf();
natoms_ = 0;
atoms_.clear();
atomBuf_.cursor_ = 0;
slices_.clear();
mainBuf.cursor_ = 0;
parserAtomMap_.clear();
return Ok();
}
XDRResult XDRIncrementalStencilEncoder::linearize(JS::TranscodeBuffer& buffer) {
switchToHeaderBuf();
uint32_t nchunks = encodedFunctions_.count() + 1;
MOZ_TRY(XDRChunkCount(this, &nchunks));
switchToMainBuf();
size_t totalLength =
buffer.length() + header_.length() + finishedChunk_.length();
if (!buffer.reserve(totalLength)) {
ReportOutOfMemory(cx());
return fail(JS::TranscodeResult_Throw);
}
buffer.infallibleAppend(header_.begin(), header_.length());
buffer.infallibleAppend(finishedChunk_.begin(), finishedChunk_.length());
return Ok();
}
void XDRDecoder::trace(JSTracer* trc) { atomTable_.trace(trc); }
void XDRIncrementalEncoder::trace(JSTracer* trc) { atomMap_.trace(trc); }
XDRResult XDRStencilDecoder::codeStencils(
frontend::CompilationInfoVector& compilationInfos) {
MOZ_ASSERT(compilationInfos.delazifications.length() == 0);
MOZ_TRY(codeStencil(compilationInfos.initial));
if (!compilationInfos.delazifications.reserve(nchunks_ - 1)) {
ReportOutOfMemory(cx());
return fail(JS::TranscodeResult_Throw);
}
for (size_t i = 1; i < nchunks_; i++) {
compilationInfos.delazifications.infallibleEmplaceBack(
cx(), compilationInfos.initial.input.options);
auto& funInfo = compilationInfos.delazifications[i - 1];
parserAtomTable_.clear();
parserAtoms_ = &funInfo.stencil.parserAtoms;
hasFinishedAtomTable_ = false;
MOZ_TRY(codeFunctionStencil(funInfo.stencil));
}
return Ok();
}
XDRResult XDRIncrementalStencilEncoder::codeStencils(
frontend::CompilationInfoVector& compilationInfos) {
MOZ_ASSERT(encodedFunctions_.count() == 0);
MOZ_TRY(codeStencil(compilationInfos.initial));
for (auto& delazification : compilationInfos.delazifications) {
MOZ_TRY(codeFunctionStencil(delazification.stencil));
}
return Ok();
}
XDRResultT<bool> XDRIncrementalStencilEncoder::checkAlreadyCoded(
const frontend::CompilationStencil& stencil) {
auto key = toFunctionKey(
stencil.scriptData[frontend::CompilationInfo::TopLevelIndex].extent);
auto p = encodedFunctions_.lookupForAdd(key);
if (p) {
return true;
}
if (!encodedFunctions_.add(p, key)) {
ReportOutOfMemory(cx());
return fail<bool>(JS::TranscodeResult_Throw);
}
return false;
}

Просмотреть файл

@ -22,19 +22,24 @@
#include "js/Transcoding.h"
#include "js/TypeDecls.h"
#include "vm/JSAtom.h"
#include "vm/SharedStencil.h" // js::SourceExtent
namespace js {
namespace frontend {
struct CompilationStencil;
struct CompilationInfo;
struct CompilationInfoVector;
struct CompilationInput;
struct CompilationStencil;
} // namespace frontend
class LifoAlloc;
enum XDRMode { XDR_ENCODE, XDR_DECODE };
using XDRResult = mozilla::Result<mozilla::Ok, JS::TranscodeResult>;
template <typename T>
using XDRResultT = mozilla::Result<T, JS::TranscodeResult>;
using XDRResult = XDRResultT<mozilla::Ok>;
using XDRAtomTable = JS::GCVector<PreBarriered<JSAtom*>>;
using XDRAtomMap = JS::GCHashMap<PreBarriered<JSAtom*>, uint32_t>;
@ -67,6 +72,8 @@ class XDRBufferBase {
#ifdef DEBUG
bool aligned_;
#endif
friend class XDRIncrementalStencilEncoder;
};
template <XDRMode mode>
@ -238,8 +245,9 @@ class XDRState : public XDRCoderBase {
JSContext* cx() const { return mainBuf.cx(); }
virtual bool isForStencil() const { return false; }
virtual frontend::CompilationInfo& stencilCompilationInfo() {
MOZ_CRASH("does not have stencil compilationInfo.");
virtual XDRResultT<bool> checkAlreadyCoded(
const frontend::CompilationStencil& stencil) {
return false;
}
virtual bool hasOptions() const { return false; }
@ -259,6 +267,9 @@ class XDRState : public XDRCoderBase {
}
virtual uint32_t& natoms() { MOZ_CRASH("does not have atomMap."); }
// The number of chunks (CompilationStencils) in the buffer.
virtual uint32_t& nchunks() { MOZ_CRASH("does not have atomMap."); }
virtual bool hasAtomTable() const { return false; }
virtual XDRAtomTable& atomTable() { MOZ_CRASH("does not have atomTable"); }
virtual frontend::ParserAtomsTable& frontendAtoms() {
@ -278,7 +289,14 @@ class XDRState : public XDRCoderBase {
MOZ_CRASH("cannot switch to header buffer.");
}
XDRResult fail(JS::TranscodeResult code) {
virtual XDRResult finishChunk() { return Ok(); }
virtual XDRResult codeDelazificationStencils(
frontend::CompilationInfoVector& compilationInfos) {
MOZ_CRASH("cannot code delazification stencils.");
}
template <typename T = mozilla::Ok>
XDRResultT<T> fail(JS::TranscodeResult code) {
#ifdef DEBUG
MOZ_ASSERT(code != JS::TranscodeResult_Ok);
MOZ_ASSERT(validateResultCode(cx(), code));
@ -450,7 +468,8 @@ class XDRState : public XDRCoderBase {
XDRResult codeFunction(JS::MutableHandleFunction objp,
HandleScriptSourceObject sourceObject = nullptr);
XDRResult codeScript(MutableHandleScript scriptp);
XDRResult codeStencil(frontend::CompilationStencil& stencil);
XDRResult codeStencil(frontend::CompilationInfo& compilationInfo);
XDRResult codeFunctionStencil(frontend::CompilationStencil& stencil);
};
using XDREncoder = XDRState<XDR_ENCODE>;
@ -478,43 +497,53 @@ class XDRDecoder : public XDRDecoderBase {
/*
* The stencil decoder accepts `options` and `range` as input, along
* with a freshly initialized `compilationInfo` and `parserAtoms` table.
* with a freshly initialized `parserAtoms` table.
*
* The decoded stencils are outputted to the default-initialized
* `compilationInfo` parameter, and decoded atoms are interned into
* the `parserAtoms` parameter.
* `compilationInfo` parameter of `codeStencil` method, and decoded atoms are
* interned into the `parserAtoms` parameter of the ctor.
*/
class XDRStencilDecoder : public XDRDecoderBase {
uint32_t nchunks_ = 0;
public:
XDRStencilDecoder(JSContext* cx, const JS::ReadOnlyCompileOptions* options,
const JS::TranscodeRange& range,
frontend::CompilationInfo& compilationInfo,
JS::TranscodeBuffer& buffer, size_t cursor,
frontend::ParserAtomsTable& parserAtoms)
: XDRDecoderBase(cx, range),
: XDRDecoderBase(cx, buffer, cursor),
options_(options),
compilationInfo_(compilationInfo),
parserAtoms_(parserAtoms) {
parserAtoms_(&parserAtoms) {
MOZ_ASSERT(options_);
}
bool isForStencil() const override { return true; }
frontend::CompilationInfo& stencilCompilationInfo() override {
return compilationInfo_;
XDRStencilDecoder(JSContext* cx, const JS::ReadOnlyCompileOptions* options,
const JS::TranscodeRange& range,
frontend::ParserAtomsTable& parserAtoms)
: XDRDecoderBase(cx, range),
options_(options),
parserAtoms_(&parserAtoms) {
MOZ_ASSERT(options_);
}
uint32_t& nchunks() override { return nchunks_; }
bool isForStencil() const override { return true; }
bool hasAtomTable() const override { return hasFinishedAtomTable_; }
frontend::ParserAtomsTable& frontendAtoms() override { return parserAtoms_; }
frontend::ParserAtomsTable& frontendAtoms() override { return *parserAtoms_; }
XDRParserAtomTable& parserAtomTable() override { return parserAtomTable_; }
void finishAtomTable() override { hasFinishedAtomTable_ = true; }
bool hasOptions() const override { return true; }
const JS::ReadOnlyCompileOptions& options() override { return *options_; }
XDRResult codeStencils(frontend::CompilationInfoVector& compilationInfos);
private:
const JS::ReadOnlyCompileOptions* options_;
XDRParserAtomTable parserAtomTable_;
bool hasFinishedAtomTable_ = false;
frontend::CompilationInfo& compilationInfo_;
frontend::ParserAtomsTable& parserAtoms_;
frontend::ParserAtomsTable* parserAtoms_;
};
class XDROffThreadDecoder : public XDRDecoder {
@ -549,7 +578,45 @@ class XDROffThreadDecoder : public XDRDecoder {
}
};
class XDRIncrementalEncoder : public XDREncoder {
class XDRIncrementalEncoderBase : public XDREncoder {
protected:
JS::TranscodeBuffer slices_;
// Atom buffer.
JS::TranscodeBuffer atoms_;
XDRBuffer<XDR_ENCODE> atomBuf_;
// Header buffer.
JS::TranscodeBuffer header_;
XDRBuffer<XDR_ENCODE> headerBuf_;
uint32_t natoms_ = 0;
public:
explicit XDRIncrementalEncoderBase(JSContext* cx)
: XDREncoder(cx, slices_, 0),
atomBuf_(cx, atoms_, 0),
headerBuf_(cx, header_, 0) {}
uint32_t& natoms() override { return natoms_; }
bool isMainBuf() override { return buf == &mainBuf; }
// Switch from streaming into the main buffer into the atom buffer.
void switchToAtomBuf() override { buf = &atomBuf_; }
// Switch to streaming into the main buffer.
void switchToMainBuf() override { buf = &mainBuf; }
// Switch to streaming into the header buffer.
void switchToHeaderBuf() override { buf = &headerBuf_; }
virtual XDRResult linearize(JS::TranscodeBuffer& buffer) {
MOZ_CRASH("cannot linearize.");
}
virtual void trace(JSTracer* trc) {}
};
class XDRIncrementalEncoder : public XDRIncrementalEncoderBase {
// The incremental encoder encodes the content of scripts and functions in
// the XDRBuffer. It can be used to encode multiple times the same
// AutoXDRTree, and uses its key to identify which part to replace.
@ -611,44 +678,24 @@ class XDRIncrementalEncoder : public XDREncoder {
SlicesNode* node_;
// Tree of slices.
SlicesTree tree_;
JS::TranscodeBuffer slices_;
// Map from atoms to their index in the atom buffer
XDRAtomMap atomMap_;
// Atom buffer.
JS::TranscodeBuffer atoms_;
XDRBuffer<XDR_ENCODE> atomBuf_;
// Header buffer.
JS::TranscodeBuffer header_;
XDRBuffer<XDR_ENCODE> headerBuf_;
bool oom_;
uint32_t natoms_ = 0;
class DepthFirstSliceIterator;
public:
explicit XDRIncrementalEncoder(JSContext* cx)
: XDREncoder(cx, slices_, 0),
: XDRIncrementalEncoderBase(cx),
scope_(nullptr),
node_(nullptr),
atomMap_(cx),
atomBuf_(cx, atoms_, 0),
headerBuf_(cx, header_, 0),
oom_(false) {}
virtual ~XDRIncrementalEncoder() = default;
bool hasAtomMap() const override { return true; }
XDRAtomMap& atomMap() override { return atomMap_; }
uint32_t& natoms() override { return natoms_; }
bool isMainBuf() override { return buf == &mainBuf; }
// Switch from streaming into the main buffer into the atom buffer.
void switchToAtomBuf() override { buf = &atomBuf_; }
// Switch to streaming into the main buffer.
void switchToMainBuf() override { buf = &mainBuf; }
// Switch to streaming into the header buffer.
void switchToHeaderBuf() override { buf = &headerBuf_; }
AutoXDRTree::Key getTopLevelTreeKey() const override;
AutoXDRTree::Key getTreeKey(JSFunction* fun) const override;
@ -658,35 +705,68 @@ class XDRIncrementalEncoder : public XDREncoder {
// Append the content collected during the incremental encoding into the
// buffer given as argument.
XDRResult linearize(JS::TranscodeBuffer& buffer);
XDRResult linearize(JS::TranscodeBuffer& buffer) override;
void trace(JSTracer* trc);
void trace(JSTracer* trc) override;
};
class XDRIncrementalStencilEncoder : public XDRIncrementalEncoder {
// The CompilationInfo for the compile. The main reason we keep a
// pointer to such a high-level struct within the encoder is to
// access the ScriptSource via `compilationInfo_.input`, as that
// needs to be encoded/decoded for XDR.
frontend::CompilationInfo& compilationInfo_;
class XDRIncrementalStencilEncoder : public XDRIncrementalEncoderBase {
// The structure of the resulting buffer is:
//
// 1. header
// a. version
// b. CompilationInput (ScriptSource)
// 2. number of chunks (initial compilation + delazification)
// 3. initial compilation chunk
// a. number of atoms
// b. atoms
// c. CompilationStencil
// 4. array of delazification chunks
// a. number of atoms
// b. atoms
// c. CompilationStencil
// Map from atoms to their index in the atom buffer
// Reset for each chunk.
XDRParserAtomMap parserAtomMap_;
JS::TranscodeBuffer finishedChunk_;
XDRBuffer<XDR_ENCODE> finishedChunkBuf_;
using FunctionKey = uint64_t;
static FunctionKey toFunctionKey(const SourceExtent& extent) {
return (FunctionKey)extent.sourceStart << 32 | extent.sourceEnd;
}
// A set of functions that is passed to codeFunctionStencil.
// Used to avoid encoding delazification for same function twice.
// NOTE: This is not a set of all encoded functions.
HashSet<FunctionKey> encodedFunctions_;
public:
XDRIncrementalStencilEncoder(JSContext* cx,
frontend::CompilationInfo& compilationInfo)
: XDRIncrementalEncoder(cx),
compilationInfo_(compilationInfo),
parserAtomMap_(cx) {}
explicit XDRIncrementalStencilEncoder(JSContext* cx)
: XDRIncrementalEncoderBase(cx),
parserAtomMap_(cx),
finishedChunkBuf_(cx, finishedChunk_, 0),
encodedFunctions_(cx) {}
virtual ~XDRIncrementalStencilEncoder() = default;
XDRResultT<bool> checkAlreadyCoded(
const frontend::CompilationStencil& stencil) override;
void switchToFinishedChunkBuf() { buf = &finishedChunkBuf_; }
bool isForStencil() const override { return true; }
frontend::CompilationInfo& stencilCompilationInfo() override {
return compilationInfo_;
}
bool hasAtomMap() const override { return true; }
XDRParserAtomMap& parserAtomMap() override { return parserAtomMap_; }
XDRResult finishChunk() override;
XDRResult linearize(JS::TranscodeBuffer& buffer) override;
XDRResult codeStencils(frontend::CompilationInfoVector& compilationInfos);
};
template <XDRMode mode>
@ -706,6 +786,10 @@ template <XDRMode mode>
XDRResult XDRParserAtomOrNull(XDRState<mode>* xdr,
const frontend::ParserAtom** atomp);
template <XDRMode mode>
XDRResult XDRCompilationInput(XDRState<mode>* xdr,
frontend::CompilationInput& input);
template <XDRMode mode>
XDRResult XDRCompilationStencil(XDRState<mode>* xdr,
frontend::CompilationStencil& stencil);