Bug 1662273 - Use stencil XDR in incremental encoding and off-thread single script decoding. r=tcampbell

Differential Revision: https://phabricator.services.mozilla.com/D91437
This commit is contained in:
Tooru Fujisawa 2020-09-28 17:14:34 +00:00
Родитель 2119eb41da
Коммит 9ef7fb0845
16 изменённых файлов: 530 добавлений и 54 удалений

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

@ -272,8 +272,8 @@ nsresult nsJSUtils::ExecutionContext::Decode(
}
MOZ_ASSERT(!mWantsReturnValue);
JS::TranscodeResult tr =
JS::DecodeScript(mCx, aBytecodeBuf, &mScript, aBytecodeIndex);
JS::TranscodeResult tr = JS::DecodeScriptMaybeStencil(
mCx, aBytecodeBuf, aCompileOptions, &mScript, aBytecodeIndex);
// These errors are external parameters which should be handled before the
// decoding phase, and which are the only reasons why you might want to
// fallback on decoding failures.

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

@ -117,6 +117,11 @@ extern JS_PUBLIC_API void CancelOffThreadModule(JSContext* cx,
extern JS_PUBLIC_API bool CanDecodeOffThread(
JSContext* cx, const ReadOnlyCompileOptions& options, size_t length);
// If options.useOffThreadParseGlobal is true,
// decode JSScript from the buffer.
//
// If options.useOffThreadParseGlobal is false,
// decode stencil from the buffer and instantiate JSScript from it.
extern JS_PUBLIC_API bool DecodeOffThreadScript(
JSContext* cx, const ReadOnlyCompileOptions& options,
mozilla::Vector<uint8_t>& buffer /* TranscodeBuffer& */, size_t cursor,
@ -135,6 +140,7 @@ extern JS_PUBLIC_API JSScript* FinishOffThreadScriptDecoder(
extern JS_PUBLIC_API void CancelOffThreadScriptDecoder(JSContext* cx,
OffThreadToken* token);
// Decode multiple JSScript from the sources.
extern JS_PUBLIC_API bool DecodeMultiOffThreadScripts(
JSContext* cx, const ReadOnlyCompileOptions& options,
mozilla::Vector<TranscodeSource>& sources,
@ -157,6 +163,11 @@ extern JS_PUBLIC_API void SetUseOffThreadParseGlobal(bool value);
} // namespace JS
namespace js {
// Returns the value set by JS::SetUseOffThreadParseGlobal.
// The default value is true.
//
// This value is consumed internally, and public API consumer shouldn't
// directly use this value.
extern bool UseOffThreadParseGlobal();
} // namespace js

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

@ -25,6 +25,8 @@ class JS_PUBLIC_API JSScript;
namespace JS {
class ReadOnlyCompileOptions;
using TranscodeBuffer = mozilla::Vector<uint8_t>;
using TranscodeRange = mozilla::Range<uint8_t>;
@ -70,14 +72,32 @@ extern JS_PUBLIC_API TranscodeResult
DecodeScript(JSContext* cx, const TranscodeRange& range,
MutableHandle<JSScript*> scriptp);
// Decode JSScript from the buffer, and register an encoder on its script
// source, such that all functions can be encoded as they are parsed. This
// strategy is used to avoid blocking the main thread in a non-interruptible
// way.
// If js::UseOffThreadParseGlobal is true, decode JSScript from the buffer.
//
// If js::UseOffThreadParseGlobal is false, decode CompilationStencil from the
// buffer and instantiate JSScript from it.
//
// options.useOffThreadParseGlobal should match JS::SetUseOffThreadParseGlobal.
extern JS_PUBLIC_API TranscodeResult DecodeScriptMaybeStencil(
JSContext* cx, TranscodeBuffer& buffer,
const ReadOnlyCompileOptions& options, MutableHandle<JSScript*> scriptp,
size_t cursorIndex = 0);
// If js::UseOffThreadParseGlobal is true, decode JSScript from the buffer.
//
// If js::UseOffThreadParseGlobal is false, decode CompilationStencil from the
// buffer and instantiate JSScript from it.
//
// And then register an encoder on its script source, such that all functions
// can be encoded as they are parsed. This strategy is used to avoid blocking
// the main thread in a non-interruptible way.
//
// See also JS::FinishIncrementalEncoding.
//
// options.useOffThreadParseGlobal should match JS::SetUseOffThreadParseGlobal.
extern JS_PUBLIC_API TranscodeResult DecodeScriptAndStartIncrementalEncoding(
JSContext* cx, TranscodeBuffer& buffer, MutableHandle<JSScript*> scriptp,
JSContext* cx, TranscodeBuffer& buffer,
const ReadOnlyCompileOptions& options, MutableHandle<JSScript*> scriptp,
size_t cursorIndex = 0);
// Finish incremental encoding started by one of:
@ -91,6 +111,11 @@ extern JS_PUBLIC_API TranscodeResult DecodeScriptAndStartIncrementalEncoding(
// The |buffer| argument of |FinishIncrementalEncoding| is used for appending
// the encoded bytecode into the buffer. If any of these functions failed, the
// content of |buffer| would be undefined.
//
// If js::UseOffThreadParseGlobal is true, |buffer| contains encoded JSScript.
//
// If js::UseOffThreadParseGlobal is false, |buffer| contains encoded
// CompilationStencil.
extern JS_PUBLIC_API bool FinishIncrementalEncoding(JSContext* cx,
Handle<JSScript*> script,
TranscodeBuffer& buffer);

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

@ -17,7 +17,7 @@
#include "jstypes.h" // JS_PUBLIC_API
#include "frontend/CompilationInfo.h" // CompilationInfo, CompilationGCOutput
#include "frontend/CompilationInfo.h" // CompilationInfo, CompilationInfoVector, CompilationGCOutput
#include "frontend/ParseContext.h" // js::frontend::UsedNameTracker
#include "frontend/SharedContext.h" // js::frontend::Directives, js::frontend::{,Eval,Global}SharedContext
#include "js/CompileOptions.h" // JS::ReadOnlyCompileOptions
@ -70,6 +70,10 @@ extern UniquePtr<CompilationInfo> CompileGlobalScriptToStencil(
extern bool InstantiateStencils(JSContext* cx, CompilationInfo& compilationInfo,
CompilationGCOutput& gcOutput);
extern bool InstantiateStencils(JSContext* cx,
CompilationInfoVector& compilationInfos,
CompilationGCOutput& gcOutput);
extern JSScript* CompileGlobalScript(JSContext* cx,
const JS::ReadOnlyCompileOptions& options,
JS::SourceText<char16_t>& srcBuf,

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

@ -346,6 +346,32 @@ bool frontend::InstantiateStencils(JSContext* cx,
return true;
}
bool frontend::InstantiateStencils(JSContext* cx,
CompilationInfoVector& compilationInfos,
CompilationGCOutput& gcOutput) {
{
AutoGeckoProfilerEntry pseudoFrame(cx, "stencil instantiate",
JS::ProfilingCategoryPair::JS_Parsing);
if (!compilationInfos.instantiateStencils(cx, gcOutput)) {
return false;
}
}
// Enqueue an off-thread source compression task after finishing parsing.
if (!cx->isHelperThreadContext()) {
if (!compilationInfos.initial.input.source()->tryCompressOffThread(cx)) {
return false;
}
}
tellDebuggerAboutCompiledScript(
cx, compilationInfos.initial.input.options.hideScriptFromDebugger,
gcOutput.script);
return true;
}
template <typename Unit>
static JSScript* CompileGlobalScriptImpl(
JSContext* cx, const JS::ReadOnlyCompileOptions& options,

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

@ -14,5 +14,6 @@ var test = (function () {
try {
evalWithCache(test, {});
} catch (x) {
assertEq(x.message.includes("Asm.js is not supported by XDR"), true);
assertEq(x.message.includes("Asm.js is not supported by XDR") ||
x.message.includes("XDR encoding failure"), true);
}

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

@ -2,9 +2,9 @@ load(libdir + "asserts.js")
// JS::EncodeScript cannot be use for run-once scripts.
evaluate(cacheEntry(""), { saveBytecode: true });
evaluate(cacheEntry(""), { saveBytecode: true, isRunOnce: false })
evaluate(cacheEntry(""), { saveBytecode: true, isRunOnce: false });
assertErrorMessage(() => {
evaluate(cacheEntry(""), { saveBytecode: true, isRunOnce: true })
evaluate(cacheEntry(""), { saveBytecode: true, isRunOnce: true });
}, Error, "run-once script are not supported by XDR");
// Incremental XDR doesn't have any of these restrictions.

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

@ -42,7 +42,9 @@
#ifdef JS_HAS_TYPED_OBJECTS
# include "builtin/TypedObject.h"
#endif
#include "frontend/BytecodeCompilation.h" // frontend::CompileGlobalScriptToStencil, frontend::InstantiateStencils
#include "frontend/BytecodeCompiler.h"
#include "frontend/CompilationInfo.h" // frontend::CompilationInfo, frontend::CompilationInfoVector, frontend::CompilationGCOutput
#include "gc/FreeOp.h"
#include "gc/Marking.h"
#include "gc/Policy.h"
@ -5719,6 +5721,57 @@ JS_PUBLIC_API JS::TranscodeResult JS::DecodeScript(
return JS::TranscodeResult_Ok;
}
static JS::TranscodeResult DecodeStencil(
JSContext* cx, JS::TranscodeBuffer& buffer,
frontend::CompilationInfoVector& compilationInfos, size_t cursorIndex) {
XDRStencilDecoder decoder(cx, &compilationInfos.initial.input.options, buffer,
cursorIndex,
compilationInfos.initial.stencil.parserAtoms);
if (!compilationInfos.initial.input.initForGlobal(cx)) {
return JS::TranscodeResult_Throw;
}
XDRResult res = decoder.codeStencils(compilationInfos);
if (res.isErr()) {
return res.unwrapErr();
}
return JS::TranscodeResult_Ok;
}
JS_PUBLIC_API JS::TranscodeResult JS::DecodeScriptMaybeStencil(
JSContext* cx, TranscodeBuffer& buffer,
const ReadOnlyCompileOptions& options, JS::MutableHandleScript scriptp,
size_t cursorIndex) {
MOZ_ASSERT(options.useOffThreadParseGlobal == js::UseOffThreadParseGlobal());
if (js::UseOffThreadParseGlobal()) {
// The buffer contains JSScript.
return JS::DecodeScript(cx, buffer, scriptp, cursorIndex);
}
// The buffer contains stencil.
Rooted<frontend::CompilationInfoVector> compilationInfos(
cx, frontend::CompilationInfoVector(cx, options));
JS::TranscodeResult res =
DecodeStencil(cx, buffer, compilationInfos.get(), cursorIndex);
if (res != JS::TranscodeResult_Ok) {
return res;
}
frontend::CompilationGCOutput gcOutput(cx);
if (!frontend::InstantiateStencils(cx, compilationInfos.get(), gcOutput)) {
return JS::TranscodeResult_Throw;
}
MOZ_ASSERT(gcOutput.script);
scriptp.set(gcOutput.script);
return JS::TranscodeResult_Ok;
}
JS_PUBLIC_API JS::TranscodeResult JS::DecodeScript(
JSContext* cx, const TranscodeRange& range,
JS::MutableHandleScript scriptp) {
@ -5737,9 +5790,13 @@ JS_PUBLIC_API JS::TranscodeResult JS::DecodeScript(
}
JS_PUBLIC_API JS::TranscodeResult JS::DecodeScriptAndStartIncrementalEncoding(
JSContext* cx, TranscodeBuffer& buffer, JS::MutableHandleScript scriptp,
JSContext* cx, TranscodeBuffer& buffer,
const ReadOnlyCompileOptions& options, JS::MutableHandleScript scriptp,
size_t cursorIndex) {
JS::TranscodeResult res = JS::DecodeScript(cx, buffer, scriptp, cursorIndex);
MOZ_ASSERT(options.useOffThreadParseGlobal == js::UseOffThreadParseGlobal());
if (js::UseOffThreadParseGlobal()) {
JS::TranscodeResult res =
JS::DecodeScript(cx, buffer, scriptp, cursorIndex);
if (res != JS::TranscodeResult_Ok) {
return res;
}
@ -5751,13 +5808,41 @@ JS_PUBLIC_API JS::TranscodeResult JS::DecodeScriptAndStartIncrementalEncoding(
return JS::TranscodeResult_Ok;
}
Rooted<frontend::CompilationInfoVector> compilationInfos(
cx, frontend::CompilationInfoVector(cx, options));
JS::TranscodeResult res =
DecodeStencil(cx, buffer, compilationInfos.get(), cursorIndex);
if (res != JS::TranscodeResult_Ok) {
return res;
}
UniquePtr<XDRIncrementalEncoderBase> xdrEncoder;
if (!compilationInfos.get().initial.input.source()->xdrEncodeStencils(
cx, compilationInfos.get(), xdrEncoder)) {
return JS::TranscodeResult_Throw;
}
frontend::CompilationGCOutput gcOutput(cx);
if (!frontend::InstantiateStencils(cx, compilationInfos.get(), gcOutput)) {
return JS::TranscodeResult_Throw;
}
MOZ_ASSERT(gcOutput.script);
gcOutput.script->scriptSource()->setIncrementalEncoder(xdrEncoder.release());
scriptp.set(gcOutput.script);
return JS::TranscodeResult_Ok;
}
JS_PUBLIC_API bool JS::FinishIncrementalEncoding(JSContext* cx,
JS::HandleScript script,
TranscodeBuffer& buffer) {
if (!script) {
return false;
}
if (!script->scriptSource()->xdrFinalizeEncoder(buffer)) {
if (!script->scriptSource()->xdrFinalizeEncoder(cx, buffer)) {
return false;
}
return true;

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

@ -122,7 +122,7 @@
#include "js/MemoryFunctions.h"
#include "js/Modules.h" // JS::GetModulePrivate, JS::SetModule{DynamicImport,Metadata,Resolve}Hook, JS::SetModulePrivate
#include "js/Object.h" // JS::GetClass, JS::GetCompartment, JS::GetReservedSlot, JS::SetReservedSlot
#include "js/OffThreadScriptCompilation.h" // JS::SetUseOffThreadParseGlobal
#include "js/OffThreadScriptCompilation.h" // JS::SetUseOffThreadParseGlobal, js::UseOffThreadParseGlobal
#include "js/Printf.h"
#include "js/PropertySpec.h"
#include "js/Realm.h"
@ -1910,9 +1910,16 @@ static void my_LargeAllocFailCallback() {
static const uint32_t CacheEntry_SOURCE = 0;
static const uint32_t CacheEntry_BYTECODE = 1;
static const uint32_t CacheEntry_KIND = 2;
enum class BytecodeCacheKind : uint32_t {
Undefined = 0,
Script,
Stencil,
};
static const JSClass CacheEntry_class = {"CacheEntryObject",
JSCLASS_HAS_RESERVED_SLOTS(2)};
JSCLASS_HAS_RESERVED_SLOTS(3)};
static bool CacheEntry(JSContext* cx, unsigned argc, JS::Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
@ -1930,6 +1937,8 @@ static bool CacheEntry(JSContext* cx, unsigned argc, JS::Value* vp) {
JS::SetReservedSlot(obj, CacheEntry_SOURCE, args[0]);
JS::SetReservedSlot(obj, CacheEntry_BYTECODE, UndefinedValue());
JS::SetReservedSlot(obj, CacheEntry_KIND,
Int32Value(int32_t(BytecodeCacheKind::Undefined)));
args.rval().setObject(*obj);
return true;
}
@ -1950,6 +1959,18 @@ static JSString* CacheEntry_getSource(JSContext* cx, HandleObject cache) {
return v.toString();
}
static BytecodeCacheKind CacheEntry_getKind(JSContext* cx, HandleObject cache) {
MOZ_ASSERT(CacheEntry_isCacheEntry(cache));
Value v = JS::GetReservedSlot(cache, CacheEntry_KIND);
return BytecodeCacheKind(v.toInt32());
}
static void CacheEntry_setKind(JSContext* cx, HandleObject cache,
BytecodeCacheKind kind) {
MOZ_ASSERT(CacheEntry_isCacheEntry(cache));
JS::SetReservedSlot(cache, CacheEntry_KIND, Int32Value(int32_t(kind)));
}
static uint8_t* CacheEntry_getBytecode(JSContext* cx, HandleObject cache,
uint32_t* length) {
MOZ_ASSERT(CacheEntry_isCacheEntry(cache));
@ -2210,6 +2231,8 @@ static bool Evaluate(JSContext* cx, unsigned argc, Value* vp) {
JS::TranscodeBuffer loadBuffer;
JS::TranscodeBuffer saveBuffer;
BytecodeCacheKind loadCacheKind = BytecodeCacheKind::Undefined;
BytecodeCacheKind saveCacheKind = BytecodeCacheKind::Undefined;
if (loadBytecode) {
uint32_t loadLength = 0;
@ -2222,6 +2245,7 @@ static bool Evaluate(JSContext* cx, unsigned argc, Value* vp) {
JS_ReportOutOfMemory(cx);
return false;
}
loadCacheKind = CacheEntry_getKind(cx, cacheEntry);
}
{
@ -2232,14 +2256,49 @@ static bool Evaluate(JSContext* cx, unsigned argc, Value* vp) {
if (loadBytecode) {
JS::TranscodeResult rv;
if (saveIncrementalBytecode) {
rv = JS::DecodeScriptAndStartIncrementalEncoding(cx, loadBuffer,
&script);
} else {
rv = JS::DecodeScript(cx, loadBuffer, &script);
if (js::UseOffThreadParseGlobal()) {
if (CacheEntry_getKind(cx, cacheEntry) !=
BytecodeCacheKind::Script) {
// NOTE: This shouldn't happen unless the cache is used across
// processes with different --no-off-thread-parse-global option.
JS_ReportErrorASCII(
cx,
"if both loadBytecode and saveIncrementalBytecode are set "
"and --no-off-thread-parse-global isn't used, bytecode "
"should be saved with saveBytecode");
return false;
}
} else {
if (CacheEntry_getKind(cx, cacheEntry) !=
BytecodeCacheKind::Stencil) {
// This can happen.
JS_ReportErrorASCII(
cx,
"if both loadBytecode and saveIncrementalBytecode are set "
"and --no-off-thread-parse-global is used, bytecode should "
"be saved with saveIncrementalBytecode");
return false;
}
}
rv = JS::DecodeScriptAndStartIncrementalEncoding(cx, loadBuffer,
options, &script);
if (!ConvertTranscodeResultToJSException(cx, rv)) {
return false;
}
} else if (loadCacheKind == BytecodeCacheKind::Script) {
rv = JS::DecodeScript(cx, loadBuffer, &script);
if (!ConvertTranscodeResultToJSException(cx, rv)) {
return false;
}
} else {
MOZ_ASSERT(loadCacheKind == BytecodeCacheKind::Stencil);
MOZ_ASSERT(!js::UseOffThreadParseGlobal());
rv = JS::DecodeScriptMaybeStencil(cx, loadBuffer, options, &script);
if (!ConvertTranscodeResultToJSException(cx, rv)) {
return false;
}
}
} else {
mozilla::Range<const char16_t> chars = codeChars.twoByteRange();
JS::SourceText<char16_t> srcBuf;
@ -2254,14 +2313,16 @@ static bool Evaluate(JSContext* cx, unsigned argc, Value* vp) {
if (saveIncrementalBytecode) {
script = JS::CompileAndStartIncrementalEncoding(cx, options, srcBuf);
} else {
script = JS::Compile(cx, options, srcBuf);
}
}
if (!script) {
return false;
}
} else {
script = JS::Compile(cx, options, srcBuf);
if (!script) {
return false;
}
}
}
}
if (displayURL && !script->scriptSource()->hasDisplayURL()) {
@ -2302,6 +2363,7 @@ static bool Evaluate(JSContext* cx, unsigned argc, Value* vp) {
if (!ConvertTranscodeResultToJSException(cx, rv)) {
return false;
}
saveCacheKind = BytecodeCacheKind::Script;
}
// Serialize the encoded bytecode, recorded before the execution, into a
@ -2310,6 +2372,11 @@ static bool Evaluate(JSContext* cx, unsigned argc, Value* vp) {
if (!FinishIncrementalEncoding(cx, script, saveBuffer)) {
return false;
}
if (js::UseOffThreadParseGlobal()) {
saveCacheKind = BytecodeCacheKind::Script;
} else {
saveCacheKind = BytecodeCacheKind::Stencil;
}
}
}
@ -2347,6 +2414,8 @@ static bool Evaluate(JSContext* cx, unsigned argc, Value* vp) {
js_free(saveData);
return false;
}
MOZ_ASSERT(saveCacheKind != BytecodeCacheKind::Undefined);
CacheEntry_setKind(cx, cacheEntry, saveCacheKind);
}
return JS_WrapValue(cx, args.rval());
@ -5756,6 +5825,10 @@ static bool OffThreadDecodeScript(JSContext* cx, unsigned argc, Value* vp) {
CompileOptions options(cx);
options.setIntroductionType("js shell offThreadDecodeScript")
.setFileAndLine("<string>", 1);
// NOTE: If --no-off-thread-parse-global is used, input can be either script
// for saveBytecode, or stencil for saveIncrementalBytecode.
options.useOffThreadParseGlobal =
CacheEntry_getKind(cx, cacheEntry) == BytecodeCacheKind::Script;
if (args.length() >= 2) {
if (args[1].isPrimitive()) {
@ -8503,8 +8576,18 @@ static const JSFunctionSpecWithHelp shell_functions[] = {
" saveBytecode: if true, and if the source is a CacheEntryObject,\n"
" the bytecode would be encoded and saved into the cache entry after\n"
" the script execution.\n"
" assertEqBytecode: if true, and if both loadBytecode and saveBytecode are \n"
" true, then the loaded bytecode and the encoded bytecode are compared.\n"
" The encoded bytecode's kind is 'script'\n"
" saveIncrementalBytecode: if true, and if the source is a\n"
" CacheEntryObject, the bytecode would be incrementally encoded and\n"
" saved into the cache entry.\n"
" If --no-off-thread-parse-global is used, the encoded bytecode's\n"
" kind is 'stencil'. If not, the encoded bytecode's kind is 'script'\n"
" If both loadBytecode and saveIncrementalBytecode are set,\n"
" and --no-off-thread-parse-global is used, the input bytecode's\n"
" kind should be 'stencil'."
" assertEqBytecode: if true, and if both loadBytecode and either\n"
" saveBytecode or saveIncrementalBytecode is true, then the loaded\n"
" bytecode and the encoded bytecode are compared.\n"
" and an assertion is raised if they differ.\n"
" envChainObject: object to put on the scope chain, with its fields added\n"
" as var bindings, akin to how elements are added to the environment in\n"

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

@ -84,14 +84,53 @@ template <typename Unit>
static JSScript* CompileSourceBufferAndStartIncrementalEncoding(
JSContext* cx, const ReadOnlyCompileOptions& options,
SourceText<Unit>& srcBuf) {
Rooted<JSScript*> script(cx, CompileSourceBuffer(cx, options, srcBuf));
ScopeKind scopeKind =
options.nonSyntacticScope ? ScopeKind::NonSyntactic : ScopeKind::Global;
MOZ_ASSERT(!cx->zone()->isAtomsZone());
AssertHeapIsIdle();
CHECK_THREAD(cx);
Rooted<frontend::CompilationInfo> compilationInfo(
cx, frontend::CompilationInfo(cx, options));
if (!compilationInfo.get().input.initForGlobal(cx)) {
return nullptr;
}
if (!frontend::CompileGlobalScriptToStencil(cx, compilationInfo.get(), srcBuf,
scopeKind)) {
return nullptr;
}
UniquePtr<XDRIncrementalEncoderBase> xdrEncoder;
MOZ_ASSERT(options.useOffThreadParseGlobal == js::UseOffThreadParseGlobal());
if (!js::UseOffThreadParseGlobal()) {
if (!compilationInfo.get().input.source()->xdrEncodeInitialStencil(
cx, compilationInfo.get(), xdrEncoder)) {
return nullptr;
}
}
frontend::CompilationGCOutput gcOutput(cx);
if (!frontend::InstantiateStencils(cx, compilationInfo.get(), gcOutput)) {
return nullptr;
}
MOZ_ASSERT(gcOutput.script);
if (!js::UseOffThreadParseGlobal()) {
gcOutput.script->scriptSource()->setIncrementalEncoder(
xdrEncoder.release());
}
Rooted<JSScript*> script(cx, gcOutput.script);
if (!script) {
return nullptr;
}
if (js::UseOffThreadParseGlobal()) {
if (!script->scriptSource()->xdrEncodeTopLevel(cx, script)) {
return nullptr;
}
}
return script;
}

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

@ -45,6 +45,7 @@ class IonFreeTask;
namespace frontend {
struct CompilationInfo;
struct CompilationInfoVector;
} // namespace frontend
namespace wasm {
@ -524,6 +525,9 @@ struct ParseTask : public mozilla::LinkedListElement<ParseTask>,
// Holds the CompilationInfo generated for the script compilation.
UniquePtr<frontend::CompilationInfo> compilationInfo_;
// Holds the CompilationInfoVector generated by decoding task.
UniquePtr<frontend::CompilationInfoVector> compilationInfos_;
// Any errors or warnings produced during compilation. These are reported
// when finishing the script.
Vector<UniquePtr<CompileError>, 0, SystemAllocPolicy> errors;

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

@ -589,6 +589,9 @@ void ParseTask::trace(JSTracer* trc) {
if (compilationInfo_) {
compilationInfo_->trace(trc);
}
if (compilationInfos_) {
compilationInfos_->trace(trc);
}
}
size_t ParseTask::sizeOfExcludingThis(
@ -688,12 +691,17 @@ void ScriptParseTask<Unit>::parse(JSContext* cx) {
}
bool ParseTask::instantiateStencils(JSContext* cx) {
if (!compilationInfo_) {
if (!compilationInfo_ && !compilationInfos_) {
return false;
}
frontend::CompilationGCOutput gcOutput(cx);
bool result = frontend::InstantiateStencils(cx, *compilationInfo_, gcOutput);
bool result;
if (compilationInfo_) {
result = frontend::InstantiateStencils(cx, *compilationInfo_, gcOutput);
} else {
result = frontend::InstantiateStencils(cx, *compilationInfos_, gcOutput);
}
// Whatever happens to the top-level script compilation (even if it fails),
// we must finish initializing the SSO. This is because there may be valid
@ -756,6 +764,33 @@ void ScriptDecodeTask::parse(JSContext* cx) {
RootedScript resultScript(cx);
Rooted<ScriptSourceObject*> sourceObject(cx);
if (!options.useOffThreadParseGlobal) {
// The buffer contains stencil.
Rooted<UniquePtr<frontend::CompilationInfoVector>> compilationInfos(
cx, js_new<frontend::CompilationInfoVector>(cx, options));
if (!compilationInfos) {
ReportOutOfMemory(cx);
return;
}
XDRStencilDecoder decoder(
cx, &compilationInfos.get()->initial.input.options, range,
compilationInfos.get()->initial.stencil.parserAtoms);
if (!compilationInfos.get()->initial.input.initForGlobal(cx)) {
return;
}
XDRResult res = decoder.codeStencils(*compilationInfos);
if (!res.isOk()) {
return;
}
compilationInfos_ = std::move(compilationInfos.get());
return;
}
// The buffer contains JSScript.
Rooted<UniquePtr<XDROffThreadDecoder>> decoder(
cx,
js::MakeUnique<XDROffThreadDecoder>(
@ -999,7 +1034,6 @@ static bool QueueOffThreadParseTask(JSContext* cx, UniquePtr<ParseTask> task) {
AutoLockHelperThreadState lock;
bool needsParseGlobal = task->options.useOffThreadParseGlobal ||
(task->kind == ParseTaskKind::ScriptDecode) ||
(task->kind == ParseTaskKind::MultiScriptsDecode);
bool mustWait =
needsParseGlobal && OffThreadParsingMustWaitForGC(cx->runtime());
@ -1038,9 +1072,8 @@ static bool StartOffThreadParseTask(JSContext* cx, UniquePtr<ParseTask> task,
gc::AutoSuppressNurseryCellAlloc noNurseryAlloc(cx);
AutoSuppressAllocationMetadataBuilder suppressMetadata(cx);
// FIXME: XDR currently requires the parse global.
bool forceParseGlobal = (task->kind == ParseTaskKind::ScriptDecode) ||
(task->kind == ParseTaskKind::MultiScriptsDecode);
// FIXME: XDR for ScriptPreloader currently requires the parse global.
bool forceParseGlobal = (task->kind == ParseTaskKind::MultiScriptsDecode);
JSObject* global = nullptr;
if (options.useOffThreadParseGlobal || forceParseGlobal) {
@ -2009,6 +2042,16 @@ JSScript* GlobalHelperThreadState::finishSingleParseTask(
if (parseTask->compilationInfo_.get() &&
!parseTask->options.useOffThreadParseGlobal) {
UniquePtr<XDRIncrementalEncoderBase> xdrEncoder;
if (startEncoding == StartEncoding::Yes) {
auto compilationInfo = parseTask->compilationInfo_.get();
if (!compilationInfo->input.source()->xdrEncodeInitialStencil(
cx, *compilationInfo, xdrEncoder)) {
return nullptr;
}
}
if (!parseTask->instantiateStencils(cx)) {
return nullptr;
}
@ -2016,6 +2059,33 @@ JSScript* GlobalHelperThreadState::finishSingleParseTask(
MOZ_RELEASE_ASSERT(parseTask->scripts.length() == 1);
script = parseTask->scripts[0];
if (startEncoding == StartEncoding::Yes) {
script->scriptSource()->setIncrementalEncoder(xdrEncoder.release());
}
} else if (parseTask->compilationInfos_.get() &&
!parseTask->options.useOffThreadParseGlobal) {
UniquePtr<XDRIncrementalEncoderBase> xdrEncoder;
if (startEncoding == StartEncoding::Yes) {
auto compilationInfos = parseTask->compilationInfos_.get();
if (!compilationInfos->initial.input.source()->xdrEncodeStencils(
cx, *compilationInfos, xdrEncoder)) {
return nullptr;
}
}
if (!parseTask->instantiateStencils(cx)) {
return nullptr;
}
MOZ_RELEASE_ASSERT(parseTask->scripts.length() == 1);
script = parseTask->scripts[0];
if (startEncoding == StartEncoding::Yes) {
script->scriptSource()->setIncrementalEncoder(xdrEncoder.release());
}
} else {
MOZ_RELEASE_ASSERT(parseTask->scripts.length() <= 1);
@ -2047,11 +2117,13 @@ JSScript* GlobalHelperThreadState::finishSingleParseTask(
}
}
if (parseTask->options.useOffThreadParseGlobal) {
if (startEncoding == StartEncoding::Yes) {
if (!script->scriptSource()->xdrEncodeTopLevel(cx, script)) {
return nullptr;
}
}
}
return script;
}

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

@ -1597,7 +1597,13 @@ bool DelazifyCanonicalScriptedFunctionImpl(JSContext* cx, HandleFunction fun,
return false;
}
// TODO: encode stencil here.
if (!js::UseOffThreadParseGlobal()) {
if (ss->hasEncoder()) {
if (!ss->xdrEncodeFunctionStencil(cx, compilationInfo.get().stencil)) {
return false;
}
}
}
if (!frontend::InstantiateStencilsForDelazify(cx, compilationInfo.get())) {
// The frontend shouldn't fail after linking the function and the
@ -1608,14 +1614,16 @@ bool DelazifyCanonicalScriptedFunctionImpl(JSContext* cx, HandleFunction fun,
}
}
if (js::UseOffThreadParseGlobal()) {
// XDR the newly delazified function.
if (ss->hasEncoder()) {
RootedScriptSourceObject sourceObject(cx,
fun->nonLazyScript()->sourceObject());
RootedScriptSourceObject sourceObject(
cx, fun->nonLazyScript()->sourceObject());
if (!ss->xdrEncodeFunction(cx, fun, sourceObject)) {
return false;
}
}
}
return true;
}

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

@ -33,6 +33,7 @@
#include "frontend/BytecodeCompiler.h"
#include "frontend/BytecodeEmitter.h"
#include "frontend/CompilationInfo.h" // frontend::CompilationStencil
#include "frontend/SharedContext.h"
#include "frontend/SourceNotes.h" // SrcNote, SrcNoteType, SrcNoteIterator
#include "gc/FreeOp.h"
@ -2229,7 +2230,7 @@ template <typename Unit>
MOZ_MUST_USE bool ScriptSource::setUncompressedSourceHelper(
JSContext* cx, EntryUnits<Unit>&& source, size_t length,
SourceRetrievable retrievable) {
auto& cache = cx->zone()->runtimeFromAnyThread()->sharedImmutableStrings();
auto& cache = cx->runtime()->sharedImmutableStrings();
auto uniqueChars = SourceTypeTraits<Unit>::toCacheable(std::move(source));
auto deduped = cache.getOrCreate(std::move(uniqueChars), length);
@ -2333,7 +2334,7 @@ MOZ_MUST_USE bool ScriptSource::initializeWithUnretrievableCompressedSource(
MOZ_ASSERT(data.is<Missing>(), "shouldn't be double-initializing");
MOZ_ASSERT(compressed != nullptr);
auto& cache = cx->zone()->runtimeFromAnyThread()->sharedImmutableStrings();
auto& cache = cx->runtime()->sharedImmutableStrings();
auto deduped = cache.getOrCreate(std::move(compressed), rawLength);
if (!deduped) {
ReportOutOfMemory(cx);
@ -2675,6 +2676,58 @@ bool ScriptSource::xdrEncodeTopLevel(JSContext* cx, HandleScript script) {
return true;
}
bool ScriptSource::xdrEncodeInitialStencil(
JSContext* cx, frontend::CompilationInfo& compilationInfo,
UniquePtr<XDRIncrementalEncoderBase>& xdrEncoder) {
// Encoding failures are reported by the xdrFinalizeEncoder function.
if (containsAsmJS()) {
return true;
}
xdrEncoder = js::MakeUnique<XDRIncrementalStencilEncoder>(cx);
if (!xdrEncoder) {
ReportOutOfMemory(cx);
return false;
}
auto failureCase = mozilla::MakeScopeExit([&] { xdrEncoder.reset(nullptr); });
XDRResult res = xdrEncoder->codeStencil(compilationInfo);
if (res.isErr()) {
// On encoding failure, let failureCase destroy encoder and return true
// to avoid failing any currently executing script.
if (res.unwrapErr() & JS::TranscodeResult_Failure) {
return true;
}
return false;
}
failureCase.release();
return true;
}
bool ScriptSource::xdrEncodeStencils(
JSContext* cx, frontend::CompilationInfoVector& compilationInfos,
UniquePtr<XDRIncrementalEncoderBase>& xdrEncoder) {
if (!xdrEncodeInitialStencil(cx, compilationInfos.initial, xdrEncoder)) {
return false;
}
for (auto& delazification : compilationInfos.delazifications) {
if (!xdrEncodeFunctionStencilWith(cx, delazification.stencil, xdrEncoder)) {
return false;
}
}
return true;
}
void ScriptSource::setIncrementalEncoder(
XDRIncrementalEncoderBase* xdrEncoder) {
xdrEncoder_.reset(xdrEncoder);
}
bool ScriptSource::xdrEncodeFunction(JSContext* cx, HandleFunction fun,
HandleScriptSourceObject sourceObject) {
MOZ_ASSERT(sourceObject->source() == this);
@ -2697,8 +2750,35 @@ bool ScriptSource::xdrEncodeFunction(JSContext* cx, HandleFunction fun,
return true;
}
bool ScriptSource::xdrFinalizeEncoder(JS::TranscodeBuffer& buffer) {
bool ScriptSource::xdrEncodeFunctionStencil(
JSContext* cx, frontend::CompilationStencil& stencil) {
MOZ_ASSERT(hasEncoder());
return xdrEncodeFunctionStencilWith(cx, stencil, xdrEncoder_);
}
bool ScriptSource::xdrEncodeFunctionStencilWith(
JSContext* cx, frontend::CompilationStencil& stencil,
UniquePtr<XDRIncrementalEncoderBase>& xdrEncoder) {
auto failureCase = mozilla::MakeScopeExit([&] { xdrEncoder.reset(nullptr); });
XDRResult res = xdrEncoder->codeFunctionStencil(stencil);
if (res.isErr()) {
// On encoding failure, let failureCase destroy encoder and return true
// to avoid failing any currently executing script.
if (res.unwrapErr() & JS::TranscodeResult_Failure) {
return true;
}
return false;
}
failureCase.release();
return true;
}
bool ScriptSource::xdrFinalizeEncoder(JSContext* cx,
JS::TranscodeBuffer& buffer) {
if (!hasEncoder()) {
JS_ReportErrorASCII(cx, "XDR encoding failure");
return false;
}

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

@ -77,6 +77,7 @@ class DebugScript;
namespace frontend {
struct CompilationInfo;
struct CompilationStencil;
struct CompilationGCOutput;
class ScriptStencil;
} // namespace frontend
@ -544,7 +545,7 @@ class ScriptSource {
// function should be recorded before their first execution.
// This value is logically owned by the canonical ScriptSourceObject, and
// will be released in the canonical SSO's finalizer.
UniquePtr<XDRIncrementalEncoder> xdrEncoder_ = nullptr;
UniquePtr<XDRIncrementalEncoderBase> xdrEncoder_ = nullptr;
// A string indicating how this source code was introduced into the system.
// This is a constant, statically allocated C string, so does not need memory
@ -1016,6 +1017,26 @@ class ScriptSource {
// successfully.
bool xdrEncodeTopLevel(JSContext* cx, HandleScript script);
// Create a new XDR encoder, and encode the stencil for the initial
// compilation. The created XDR encoder isn't stored into `xdrEncoder_`
// field. Caller is responsible for calling `setIncrementalEncoder` after
// instantiating stencil (so, corresponding canonical ScriptSourceObject
// gets created).
bool xdrEncodeInitialStencil(
JSContext* cx, frontend::CompilationInfo& compilationInfo,
UniquePtr<XDRIncrementalEncoderBase>& xdrEncoder);
// Create a new XDR encoder, and encode the stencils.
// The created XDR encoder isn't stored into `xdrEncoder_` field.
// Caller is responsible for calling `setIncrementalEncoder` after
// instantiating stencil (so, corresponding canonical ScriptSourceObject
// gets created).
bool xdrEncodeStencils(JSContext* cx,
frontend::CompilationInfoVector& compilationInfos,
UniquePtr<XDRIncrementalEncoderBase>& xdrEncoder);
void setIncrementalEncoder(XDRIncrementalEncoderBase* xdrEncoder);
// Encode a delazified JSFunction. In case of errors, the XDR encoder is
// freed and the |buffer| provided as argument to |xdrEncodeTopLevel| is
// considered undefined.
@ -1025,10 +1046,23 @@ class ScriptSource {
bool xdrEncodeFunction(JSContext* cx, HandleFunction fun,
HandleScriptSourceObject sourceObject);
// Encode a delazified function's stencil. In case of errors, the XDR
// encoder is freed.
bool xdrEncodeFunctionStencil(JSContext* cx,
frontend::CompilationStencil& stencil);
private:
// Encode a delazified function's stencil. In case of errors, the passed
// XDR encoder is freed.
bool xdrEncodeFunctionStencilWith(
JSContext* cx, frontend::CompilationStencil& stencil,
UniquePtr<XDRIncrementalEncoderBase>& xdrEncoder);
public:
// 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(JS::TranscodeBuffer& buffer);
bool xdrFinalizeEncoder(JSContext* cx, JS::TranscodeBuffer& buffer);
private:
template <typename Unit,

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

@ -181,7 +181,7 @@ JS_PUBLIC_API bool JS::GetScriptTranscodingBuildId(
// Note: the buildId returned here is also used for the bytecode cache MIME
// type so use plain ASCII characters.
if (!buildId->reserve(buildId->length() + 4)) {
if (!buildId->reserve(buildId->length() + 5)) {
return false;
}
@ -196,6 +196,10 @@ JS_PUBLIC_API bool JS::GetScriptTranscodingBuildId(
// copy-on-write arrays).
buildId->infallibleAppend(IsTypeInferenceEnabled() ? '1' : '0');
// If off-thread parse global isn't used for single script decoding,
// we use stencil XDR instead of JSScript XDR.
buildId->infallibleAppend(js::UseOffThreadParseGlobal() ? '1' : '0');
return true;
}