зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1662274 - Support incremental XDR encoding of stencil. r=tcampbell
Differential Revision: https://phabricator.services.mozilla.com/D91436
This commit is contained in:
Родитель
9e6d052207
Коммит
2119eb41da
|
@ -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;
|
||||
}
|
||||
|
|
200
js/src/vm/Xdr.h
200
js/src/vm/Xdr.h
|
@ -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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче