summaryrefslogtreecommitdiffstats
path: root/js/src/frontend/BytecodeCompiler.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /js/src/frontend/BytecodeCompiler.cpp
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/frontend/BytecodeCompiler.cpp')
-rw-r--r--js/src/frontend/BytecodeCompiler.cpp1565
1 files changed, 1565 insertions, 0 deletions
diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp
new file mode 100644
index 0000000000..e4c709e7e7
--- /dev/null
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -0,0 +1,1565 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "frontend/BytecodeCompiler.h"
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/Utf8.h" // mozilla::Utf8Unit
+#include "mozilla/Variant.h" // mozilla::Variant
+
+#include "debugger/DebugAPI.h"
+#include "ds/LifoAlloc.h"
+#include "frontend/BytecodeCompilation.h"
+#include "frontend/BytecodeEmitter.h"
+#include "frontend/CompilationStencil.h"
+#include "frontend/EitherParser.h"
+#ifdef JS_ENABLE_SMOOSH
+# include "frontend/Frontend2.h" // Smoosh
+#endif
+#include "frontend/FrontendContext.h" // AutoReportFrontendContext
+#include "frontend/ModuleSharedContext.h"
+#include "js/experimental/JSStencil.h"
+#include "js/Modules.h" // JS::ImportAssertionVector
+#include "js/SourceText.h"
+#include "js/UniquePtr.h"
+#include "vm/FunctionFlags.h" // FunctionFlags
+#include "vm/GeneratorAndAsyncKind.h" // js::GeneratorKind, js::FunctionAsyncKind
+#include "vm/HelperThreads.h" // StartOffThreadDelazification, WaitForAllDelazifyTasks
+#include "vm/JSContext.h"
+#include "vm/JSScript.h" // ScriptSource, UncompressedSourceCache
+#include "vm/ModuleBuilder.h" // js::ModuleBuilder
+#include "vm/Time.h" // AutoIncrementalTimer
+#include "wasm/AsmJS.h"
+
+#include "vm/GeckoProfiler-inl.h"
+#include "vm/JSContext-inl.h"
+
+using namespace js;
+using namespace js::frontend;
+
+using mozilla::Maybe;
+using mozilla::Utf8Unit;
+
+using JS::CompileOptions;
+using JS::ReadOnlyCompileOptions;
+using JS::SourceText;
+
+// RAII class to check the frontend reports an exception when it fails to
+// compile a script.
+class MOZ_RAII AutoAssertReportedException {
+#ifdef DEBUG
+ JSContext* maybeCx_;
+ FrontendContext* fc_;
+ bool check_;
+
+ public:
+ explicit AutoAssertReportedException(JSContext* maybeCx, FrontendContext* fc)
+ : maybeCx_(maybeCx), fc_(fc), check_(true) {}
+ void reset() { check_ = false; }
+ ~AutoAssertReportedException() {
+ if (!check_) {
+ return;
+ }
+
+ // Error while compiling self-hosted code isn't set as an exception.
+ // TODO: Remove this once all errors are added to frontend context.
+ if (maybeCx_ && !maybeCx_->runtime()->hasInitializedSelfHosting()) {
+ return;
+ }
+
+ // TODO: Remove this once JSContext is removed from frontend.
+ if (maybeCx_ && !maybeCx_->isHelperThreadContext()) {
+ MOZ_ASSERT(maybeCx_->isExceptionPending() || fc_->hadErrors());
+ } else {
+ MOZ_ASSERT(fc_->hadErrors());
+ }
+ }
+#else
+ public:
+ explicit AutoAssertReportedException(JSContext*, FrontendContext*) {}
+ void reset() {}
+#endif
+};
+
+static bool EmplaceEmitter(CompilationState& compilationState,
+ Maybe<BytecodeEmitter>& emitter, FrontendContext* fc,
+ const EitherParser& parser, SharedContext* sc);
+
+template <typename Unit>
+class MOZ_STACK_CLASS SourceAwareCompiler {
+ protected:
+ SourceText<Unit>& sourceBuffer_;
+
+ CompilationState compilationState_;
+
+ Maybe<Parser<SyntaxParseHandler, Unit>> syntaxParser;
+ Maybe<Parser<FullParseHandler, Unit>> parser;
+ FrontendContext* fc_ = nullptr;
+
+ using TokenStreamPosition = frontend::TokenStreamPosition<Unit>;
+
+ protected:
+ explicit SourceAwareCompiler(FrontendContext* fc,
+ LifoAllocScope& parserAllocScope,
+ CompilationInput& input,
+ SourceText<Unit>& sourceBuffer)
+ : sourceBuffer_(sourceBuffer),
+ compilationState_(fc, parserAllocScope, input) {
+ MOZ_ASSERT(sourceBuffer_.get() != nullptr);
+ }
+
+ [[nodiscard]] bool init(FrontendContext* fc, ScopeBindingCache* scopeCache,
+ InheritThis inheritThis = InheritThis::No,
+ JSObject* enclosingEnv = nullptr) {
+ if (!compilationState_.init(fc, scopeCache, inheritThis, enclosingEnv)) {
+ return false;
+ }
+
+ return createSourceAndParser(fc);
+ }
+
+ // Call this before calling compile{Global,Eval}Script.
+ [[nodiscard]] bool createSourceAndParser(FrontendContext* fc);
+
+ void assertSourceAndParserCreated() const {
+ MOZ_ASSERT(compilationState_.source != nullptr);
+ MOZ_ASSERT(parser.isSome());
+ }
+
+ void assertSourceParserAndScriptCreated() { assertSourceAndParserCreated(); }
+
+ [[nodiscard]] bool emplaceEmitter(Maybe<BytecodeEmitter>& emitter,
+ SharedContext* sharedContext) {
+ return EmplaceEmitter(compilationState_, emitter, fc_,
+ EitherParser(parser.ptr()), sharedContext);
+ }
+
+ bool canHandleParseFailure(const Directives& newDirectives);
+
+ void handleParseFailure(
+ const Directives& newDirectives, TokenStreamPosition& startPosition,
+ CompilationState::CompilationStatePosition& startStatePosition);
+
+ public:
+ CompilationState& compilationState() { return compilationState_; };
+
+ ExtensibleCompilationStencil& stencil() { return compilationState_; }
+};
+
+template <typename Unit>
+class MOZ_STACK_CLASS ScriptCompiler : public SourceAwareCompiler<Unit> {
+ using Base = SourceAwareCompiler<Unit>;
+
+ protected:
+ using Base::compilationState_;
+ using Base::parser;
+ using Base::sourceBuffer_;
+
+ using Base::assertSourceParserAndScriptCreated;
+ using Base::canHandleParseFailure;
+ using Base::emplaceEmitter;
+ using Base::handleParseFailure;
+
+ using typename Base::TokenStreamPosition;
+
+ public:
+ explicit ScriptCompiler(FrontendContext* fc, LifoAllocScope& parserAllocScope,
+ CompilationInput& input,
+ SourceText<Unit>& sourceBuffer)
+ : Base(fc, parserAllocScope, input, sourceBuffer) {}
+
+ using Base::init;
+ using Base::stencil;
+
+ [[nodiscard]] bool compile(JSContext* cx, SharedContext* sc);
+};
+
+#ifdef JS_ENABLE_SMOOSH
+[[nodiscard]] static bool TrySmoosh(
+ JSContext* cx, FrontendContext* fc, CompilationInput& input,
+ JS::SourceText<mozilla::Utf8Unit>& srcBuf,
+ UniquePtr<ExtensibleCompilationStencil>& stencilOut) {
+ MOZ_ASSERT(!stencilOut);
+
+ if (!cx->options().trySmoosh()) {
+ return true;
+ }
+
+ JSRuntime* rt = cx->runtime();
+ if (!Smoosh::tryCompileGlobalScriptToExtensibleStencil(cx, fc, input, srcBuf,
+ stencilOut)) {
+ return false;
+ }
+
+ if (cx->options().trackNotImplemented()) {
+ if (stencilOut) {
+ rt->parserWatcherFile.put("1");
+ } else {
+ rt->parserWatcherFile.put("0");
+ }
+ }
+
+ if (!stencilOut) {
+ fprintf(stderr, "Falling back!\n");
+ return true;
+ }
+
+ return stencilOut->source->assignSource(fc, input.options, srcBuf);
+}
+
+[[nodiscard]] static bool TrySmoosh(
+ JSContext* cx, FrontendContext* fc, CompilationInput& input,
+ JS::SourceText<char16_t>& srcBuf,
+ UniquePtr<ExtensibleCompilationStencil>& stencilOut) {
+ MOZ_ASSERT(!stencilOut);
+ return true;
+}
+#endif // JS_ENABLE_SMOOSH
+
+using BytecodeCompilerOutput =
+ mozilla::Variant<UniquePtr<ExtensibleCompilationStencil>,
+ RefPtr<CompilationStencil>, CompilationGCOutput*>;
+
+// Compile global script, and return it as one of:
+// * ExtensibleCompilationStencil (without instantiation)
+// * CompilationStencil (without instantiation, has no external dependency)
+// * CompilationGCOutput (with instantiation).
+template <typename Unit>
+[[nodiscard]] static bool CompileGlobalScriptToStencilAndMaybeInstantiate(
+ JSContext* maybeCx, FrontendContext* fc, js::LifoAlloc& tempLifoAlloc,
+ CompilationInput& input, ScopeBindingCache* scopeCache,
+ JS::SourceText<Unit>& srcBuf, ScopeKind scopeKind,
+ BytecodeCompilerOutput& output) {
+#ifdef JS_ENABLE_SMOOSH
+ if (maybeCx) {
+ UniquePtr<ExtensibleCompilationStencil> extensibleStencil;
+ if (!TrySmoosh(maybeCx, fc, input, srcBuf, extensibleStencil)) {
+ return false;
+ }
+ if (extensibleStencil) {
+ if (input.options.populateDelazificationCache() &&
+ !maybeCx->isHelperThreadContext()) {
+ BorrowingCompilationStencil borrowingStencil(*extensibleStencil);
+ StartOffThreadDelazification(maybeCx, input.options, borrowingStencil);
+
+ // When we are trying to validate whether on-demand delazification
+ // generate the same stencil as concurrent delazification, we want to
+ // parse everything eagerly off-thread ahead of re-parsing everything on
+ // demand, to compare the outcome.
+ if (input.options.waitForDelazificationCache()) {
+ WaitForAllDelazifyTasks(maybeCx->runtime());
+ }
+ }
+ if (output.is<UniquePtr<ExtensibleCompilationStencil>>()) {
+ output.as<UniquePtr<ExtensibleCompilationStencil>>() =
+ std::move(extensibleStencil);
+ } else if (output.is<RefPtr<CompilationStencil>>()) {
+ RefPtr<CompilationStencil> stencil =
+ fc->getAllocator()->new_<frontend::CompilationStencil>(
+ std::move(extensibleStencil));
+ if (!stencil) {
+ return false;
+ }
+
+ output.as<RefPtr<CompilationStencil>>() = std::move(stencil);
+ } else {
+ BorrowingCompilationStencil borrowingStencil(*extensibleStencil);
+ if (!InstantiateStencils(maybeCx, input, borrowingStencil,
+ *(output.as<CompilationGCOutput*>()))) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+#endif // JS_ENABLE_SMOOSH
+
+ if (input.options.selfHostingMode) {
+ if (!input.initForSelfHostingGlobal(fc)) {
+ return false;
+ }
+ } else {
+ if (!input.initForGlobal(fc)) {
+ return false;
+ }
+ }
+
+ AutoAssertReportedException assertException(maybeCx, fc);
+
+ LifoAllocScope parserAllocScope(&tempLifoAlloc);
+ ScriptCompiler<Unit> compiler(fc, parserAllocScope, input, srcBuf);
+ if (!compiler.init(fc, scopeCache)) {
+ return false;
+ }
+
+ SourceExtent extent = SourceExtent::makeGlobalExtent(
+ srcBuf.length(), input.options.lineno, input.options.column);
+
+ GlobalSharedContext globalsc(fc, scopeKind, input.options,
+ compiler.compilationState().directives, extent);
+
+ if (!compiler.compile(maybeCx, &globalsc)) {
+ return false;
+ }
+
+ if (input.options.populateDelazificationCache() && maybeCx &&
+ !maybeCx->isHelperThreadContext()) {
+ BorrowingCompilationStencil borrowingStencil(compiler.stencil());
+ StartOffThreadDelazification(maybeCx, input.options, borrowingStencil);
+
+ // When we are trying to validate whether on-demand delazification
+ // generate the same stencil as concurrent delazification, we want to
+ // parse everything eagerly off-thread ahead of re-parsing everything on
+ // demand, to compare the outcome.
+ if (input.options.waitForDelazificationCache()) {
+ WaitForAllDelazifyTasks(maybeCx->runtime());
+ }
+ }
+
+ if (output.is<UniquePtr<ExtensibleCompilationStencil>>()) {
+ auto stencil =
+ fc->getAllocator()->make_unique<ExtensibleCompilationStencil>(
+ std::move(compiler.stencil()));
+ if (!stencil) {
+ return false;
+ }
+ output.as<UniquePtr<ExtensibleCompilationStencil>>() = std::move(stencil);
+ } else if (output.is<RefPtr<CompilationStencil>>()) {
+ Maybe<AutoGeckoProfilerEntry> pseudoFrame;
+ if (maybeCx) {
+ pseudoFrame.emplace(maybeCx, "script emit",
+ JS::ProfilingCategoryPair::JS_Parsing);
+ }
+
+ auto extensibleStencil =
+ fc->getAllocator()->make_unique<frontend::ExtensibleCompilationStencil>(
+ std::move(compiler.stencil()));
+ if (!extensibleStencil) {
+ return false;
+ }
+
+ RefPtr<CompilationStencil> stencil =
+ fc->getAllocator()->new_<CompilationStencil>(
+ std::move(extensibleStencil));
+ if (!stencil) {
+ return false;
+ }
+
+ output.as<RefPtr<CompilationStencil>>() = std::move(stencil);
+ } else {
+ MOZ_ASSERT(maybeCx);
+ BorrowingCompilationStencil borrowingStencil(compiler.stencil());
+ if (!InstantiateStencils(maybeCx, input, borrowingStencil,
+ *(output.as<CompilationGCOutput*>()))) {
+ return false;
+ }
+ }
+
+ assertException.reset();
+ return true;
+}
+
+template <typename Unit>
+static already_AddRefed<CompilationStencil> CompileGlobalScriptToStencilImpl(
+ JSContext* maybeCx, FrontendContext* fc, js::LifoAlloc& tempLifoAlloc,
+ CompilationInput& input, ScopeBindingCache* scopeCache,
+ JS::SourceText<Unit>& srcBuf, ScopeKind scopeKind) {
+ using OutputType = RefPtr<CompilationStencil>;
+ BytecodeCompilerOutput output((OutputType()));
+ if (!CompileGlobalScriptToStencilAndMaybeInstantiate(
+ maybeCx, fc, tempLifoAlloc, input, scopeCache, srcBuf, scopeKind,
+ output)) {
+ return nullptr;
+ }
+ return output.as<OutputType>().forget();
+}
+
+already_AddRefed<CompilationStencil> frontend::CompileGlobalScriptToStencil(
+ JSContext* cx, FrontendContext* fc, js::LifoAlloc& tempLifoAlloc,
+ CompilationInput& input, ScopeBindingCache* scopeCache,
+ JS::SourceText<char16_t>& srcBuf, ScopeKind scopeKind) {
+ return CompileGlobalScriptToStencilImpl(cx, fc, tempLifoAlloc, input,
+ scopeCache, srcBuf, scopeKind);
+}
+
+already_AddRefed<CompilationStencil> frontend::CompileGlobalScriptToStencil(
+ JSContext* cx, FrontendContext* fc, js::LifoAlloc& tempLifoAlloc,
+ CompilationInput& input, ScopeBindingCache* scopeCache,
+ JS::SourceText<Utf8Unit>& srcBuf, ScopeKind scopeKind) {
+ return CompileGlobalScriptToStencilImpl(cx, fc, tempLifoAlloc, input,
+ scopeCache, srcBuf, scopeKind);
+}
+
+template <typename Unit>
+static UniquePtr<ExtensibleCompilationStencil>
+CompileGlobalScriptToExtensibleStencilImpl(JSContext* maybeCx,
+ FrontendContext* fc,
+ CompilationInput& input,
+ ScopeBindingCache* scopeCache,
+ JS::SourceText<Unit>& srcBuf,
+ ScopeKind scopeKind) {
+ using OutputType = UniquePtr<ExtensibleCompilationStencil>;
+ BytecodeCompilerOutput output((OutputType()));
+ if (!CompileGlobalScriptToStencilAndMaybeInstantiate(
+ maybeCx, fc, maybeCx->tempLifoAlloc(), input, scopeCache, srcBuf,
+ scopeKind, output)) {
+ return nullptr;
+ }
+ return std::move(output.as<OutputType>());
+}
+
+UniquePtr<ExtensibleCompilationStencil>
+frontend::CompileGlobalScriptToExtensibleStencil(
+ JSContext* maybeCx, FrontendContext* fc, CompilationInput& input,
+ ScopeBindingCache* scopeCache, JS::SourceText<char16_t>& srcBuf,
+ ScopeKind scopeKind) {
+ return CompileGlobalScriptToExtensibleStencilImpl(
+ maybeCx, fc, input, scopeCache, srcBuf, scopeKind);
+}
+
+UniquePtr<ExtensibleCompilationStencil>
+frontend::CompileGlobalScriptToExtensibleStencil(
+ JSContext* cx, FrontendContext* fc, CompilationInput& input,
+ ScopeBindingCache* scopeCache, JS::SourceText<Utf8Unit>& srcBuf,
+ ScopeKind scopeKind) {
+ return CompileGlobalScriptToExtensibleStencilImpl(cx, fc, input, scopeCache,
+ srcBuf, scopeKind);
+}
+
+bool frontend::InstantiateStencils(JSContext* cx, CompilationInput& input,
+ const CompilationStencil& stencil,
+ CompilationGCOutput& gcOutput) {
+ {
+ AutoGeckoProfilerEntry pseudoFrame(cx, "stencil instantiate",
+ JS::ProfilingCategoryPair::JS_Parsing);
+
+ if (!CompilationStencil::instantiateStencils(cx, input, stencil,
+ gcOutput)) {
+ return false;
+ }
+ }
+
+ // Enqueue an off-thread source compression task after finishing parsing.
+ if (!cx->isHelperThreadContext()) {
+ if (!stencil.source->tryCompressOffThread(cx)) {
+ return false;
+ }
+
+ Rooted<JSScript*> script(cx, gcOutput.script);
+ const JS::InstantiateOptions instantiateOptions(input.options);
+ FireOnNewScript(cx, instantiateOptions, script);
+ }
+
+ return true;
+}
+
+bool frontend::PrepareForInstantiate(JSContext* maybeCx, FrontendContext* fc,
+ CompilationInput& input,
+ const CompilationStencil& stencil,
+ CompilationGCOutput& gcOutput) {
+ Maybe<AutoGeckoProfilerEntry> pseudoFrame;
+ if (maybeCx) {
+ pseudoFrame.emplace(maybeCx, "stencil instantiate",
+ JS::ProfilingCategoryPair::JS_Parsing);
+ }
+
+ return CompilationStencil::prepareForInstantiate(fc, input.atomCache, stencil,
+ gcOutput);
+}
+
+template <typename Unit>
+static JSScript* CompileGlobalScriptImpl(
+ JSContext* cx, FrontendContext* fc,
+ const JS::ReadOnlyCompileOptions& options, JS::SourceText<Unit>& srcBuf,
+ ScopeKind scopeKind) {
+ Rooted<CompilationInput> input(cx, CompilationInput(options));
+ Rooted<CompilationGCOutput> gcOutput(cx);
+ BytecodeCompilerOutput output(gcOutput.address());
+ NoScopeBindingCache scopeCache;
+ if (!CompileGlobalScriptToStencilAndMaybeInstantiate(
+ cx, fc, cx->tempLifoAlloc(), input.get(), &scopeCache, srcBuf,
+ scopeKind, output)) {
+ return nullptr;
+ }
+ return gcOutput.get().script;
+}
+
+JSScript* frontend::CompileGlobalScript(
+ JSContext* cx, FrontendContext* fc,
+ const JS::ReadOnlyCompileOptions& options, JS::SourceText<char16_t>& srcBuf,
+ ScopeKind scopeKind) {
+ return CompileGlobalScriptImpl(cx, fc, options, srcBuf, scopeKind);
+}
+
+JSScript* frontend::CompileGlobalScript(
+ JSContext* cx, FrontendContext* fc,
+ const JS::ReadOnlyCompileOptions& options, JS::SourceText<Utf8Unit>& srcBuf,
+ ScopeKind scopeKind) {
+ return CompileGlobalScriptImpl(cx, fc, options, srcBuf, scopeKind);
+}
+
+template <typename Unit>
+static JSScript* CompileEvalScriptImpl(
+ JSContext* cx, const JS::ReadOnlyCompileOptions& options,
+ SourceText<Unit>& srcBuf, JS::Handle<js::Scope*> enclosingScope,
+ JS::Handle<JSObject*> enclosingEnv) {
+ JS::Rooted<JSScript*> script(cx);
+ {
+ AutoReportFrontendContext fc(cx);
+ AutoAssertReportedException assertException(cx, &fc);
+
+ Rooted<CompilationInput> input(cx, CompilationInput(options));
+ if (!input.get().initForEval(&fc, enclosingScope)) {
+ return nullptr;
+ }
+
+ LifoAllocScope parserAllocScope(&cx->tempLifoAlloc());
+
+ ScopeBindingCache* scopeCache = &cx->caches().scopeCache;
+ ScriptCompiler<Unit> compiler(&fc, parserAllocScope, input.get(), srcBuf);
+ if (!compiler.init(&fc, scopeCache, InheritThis::Yes, enclosingEnv)) {
+ return nullptr;
+ }
+
+ uint32_t len = srcBuf.length();
+ SourceExtent extent =
+ SourceExtent::makeGlobalExtent(len, options.lineno, options.column);
+ EvalSharedContext evalsc(&fc, compiler.compilationState(), extent);
+ if (!compiler.compile(cx, &evalsc)) {
+ return nullptr;
+ }
+
+ Rooted<CompilationGCOutput> gcOutput(cx);
+ {
+ BorrowingCompilationStencil borrowingStencil(compiler.stencil());
+ if (!InstantiateStencils(cx, input.get(), borrowingStencil,
+ gcOutput.get())) {
+ return nullptr;
+ }
+ }
+
+ assertException.reset();
+ script = gcOutput.get().script;
+ }
+ return script;
+}
+
+JSScript* frontend::CompileEvalScript(JSContext* cx,
+ const JS::ReadOnlyCompileOptions& options,
+ JS::SourceText<char16_t>& srcBuf,
+ JS::Handle<js::Scope*> enclosingScope,
+ JS::Handle<JSObject*> enclosingEnv) {
+ return CompileEvalScriptImpl(cx, options, srcBuf, enclosingScope,
+ enclosingEnv);
+}
+
+template <typename Unit>
+class MOZ_STACK_CLASS ModuleCompiler final : public SourceAwareCompiler<Unit> {
+ using Base = SourceAwareCompiler<Unit>;
+
+ using Base::assertSourceParserAndScriptCreated;
+ using Base::compilationState_;
+ using Base::emplaceEmitter;
+ using Base::parser;
+
+ public:
+ explicit ModuleCompiler(FrontendContext* fc, LifoAllocScope& parserAllocScope,
+ CompilationInput& input,
+ SourceText<Unit>& sourceBuffer)
+ : Base(fc, parserAllocScope, input, sourceBuffer) {}
+
+ using Base::init;
+ using Base::stencil;
+
+ [[nodiscard]] bool compile(JSContext* maybeCx, FrontendContext* fc);
+};
+
+template <typename Unit>
+class MOZ_STACK_CLASS StandaloneFunctionCompiler final
+ : public SourceAwareCompiler<Unit> {
+ using Base = SourceAwareCompiler<Unit>;
+
+ using Base::assertSourceAndParserCreated;
+ using Base::canHandleParseFailure;
+ using Base::compilationState_;
+ using Base::emplaceEmitter;
+ using Base::handleParseFailure;
+ using Base::parser;
+ using Base::sourceBuffer_;
+
+ using typename Base::TokenStreamPosition;
+
+ public:
+ explicit StandaloneFunctionCompiler(FrontendContext* fc,
+ LifoAllocScope& parserAllocScope,
+ CompilationInput& input,
+ SourceText<Unit>& sourceBuffer)
+ : Base(fc, parserAllocScope, input, sourceBuffer) {}
+
+ using Base::init;
+ using Base::stencil;
+
+ private:
+ FunctionNode* parse(JSContext* cx, FunctionSyntaxKind syntaxKind,
+ GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
+ const Maybe<uint32_t>& parameterListEnd);
+
+ public:
+ [[nodiscard]] bool compile(JSContext* cx, FunctionSyntaxKind syntaxKind,
+ GeneratorKind generatorKind,
+ FunctionAsyncKind asyncKind,
+ const Maybe<uint32_t>& parameterListEnd);
+};
+
+template <typename Unit>
+bool SourceAwareCompiler<Unit>::createSourceAndParser(FrontendContext* fc) {
+ const auto& options = compilationState_.input.options;
+
+ fc_ = fc;
+
+ if (!compilationState_.source->assignSource(fc, options, sourceBuffer_)) {
+ return false;
+ }
+
+ MOZ_ASSERT(compilationState_.canLazilyParse ==
+ CanLazilyParse(compilationState_.input.options));
+ if (compilationState_.canLazilyParse) {
+ syntaxParser.emplace(fc_, options, sourceBuffer_.units(),
+ sourceBuffer_.length(),
+ /* foldConstants = */ false, compilationState_,
+ /* syntaxParser = */ nullptr);
+ if (!syntaxParser->checkOptions()) {
+ return false;
+ }
+ }
+
+ parser.emplace(fc_, options, sourceBuffer_.units(), sourceBuffer_.length(),
+ /* foldConstants = */ true, compilationState_,
+ syntaxParser.ptrOr(nullptr));
+ parser->ss = compilationState_.source.get();
+ return parser->checkOptions();
+}
+
+static bool EmplaceEmitter(CompilationState& compilationState,
+ Maybe<BytecodeEmitter>& emitter, FrontendContext* fc,
+ const EitherParser& parser, SharedContext* sc) {
+ BytecodeEmitter::EmitterMode emitterMode =
+ sc->selfHosted() ? BytecodeEmitter::SelfHosting : BytecodeEmitter::Normal;
+ emitter.emplace(fc, parser, sc, compilationState, emitterMode);
+ return emitter->init();
+}
+
+template <typename Unit>
+bool SourceAwareCompiler<Unit>::canHandleParseFailure(
+ const Directives& newDirectives) {
+ // Try to reparse if no parse errors were thrown and the directives changed.
+ //
+ // NOTE:
+ // Only the following two directive changes force us to reparse the script:
+ // - The "use asm" directive was encountered.
+ // - The "use strict" directive was encountered and duplicate parameter names
+ // are present. We reparse in this case to display the error at the correct
+ // source location. See |Parser::hasValidSimpleStrictParameterNames()|.
+ return !parser->anyChars.hadError() &&
+ compilationState_.directives != newDirectives;
+}
+
+template <typename Unit>
+void SourceAwareCompiler<Unit>::handleParseFailure(
+ const Directives& newDirectives, TokenStreamPosition& startPosition,
+ CompilationState::CompilationStatePosition& startStatePosition) {
+ MOZ_ASSERT(canHandleParseFailure(newDirectives));
+
+ // Rewind to starting position to retry.
+ parser->tokenStream.rewind(startPosition);
+ compilationState_.rewind(startStatePosition);
+
+ // Assignment must be monotonic to prevent reparsing iloops
+ MOZ_ASSERT_IF(compilationState_.directives.strict(), newDirectives.strict());
+ MOZ_ASSERT_IF(compilationState_.directives.asmJS(), newDirectives.asmJS());
+ compilationState_.directives = newDirectives;
+}
+
+template <typename Unit>
+bool ScriptCompiler<Unit>::compile(JSContext* maybeCx, SharedContext* sc) {
+ assertSourceParserAndScriptCreated();
+
+ TokenStreamPosition startPosition(parser->tokenStream);
+
+ // Emplace the topLevel stencil
+ MOZ_ASSERT(compilationState_.scriptData.length() ==
+ CompilationStencil::TopLevelIndex);
+ if (!compilationState_.appendScriptStencilAndData(sc->fc_)) {
+ return false;
+ }
+
+ ParseNode* pn;
+ {
+ Maybe<AutoGeckoProfilerEntry> pseudoFrame;
+ if (maybeCx) {
+ pseudoFrame.emplace(maybeCx, "script parsing",
+ JS::ProfilingCategoryPair::JS_Parsing);
+ }
+ if (sc->isEvalContext()) {
+ pn = parser->evalBody(sc->asEvalContext());
+ } else {
+ pn = parser->globalBody(sc->asGlobalContext());
+ }
+ }
+
+ if (!pn) {
+ // Global and eval scripts don't get reparsed after a new directive was
+ // encountered:
+ // - "use strict" doesn't require any special error reporting for scripts.
+ // - "use asm" directives don't have an effect in global/eval contexts.
+ MOZ_ASSERT(!canHandleParseFailure(compilationState_.directives));
+ return false;
+ }
+
+ {
+ // Successfully parsed. Emit the script.
+ Maybe<AutoGeckoProfilerEntry> pseudoFrame;
+ if (maybeCx) {
+ pseudoFrame.emplace(maybeCx, "script emit",
+ JS::ProfilingCategoryPair::JS_Parsing);
+ }
+
+ Maybe<BytecodeEmitter> emitter;
+ if (!emplaceEmitter(emitter, sc)) {
+ return false;
+ }
+
+ if (!emitter->emitScript(pn)) {
+ return false;
+ }
+ }
+
+ MOZ_ASSERT(!this->fc_->hadErrors());
+
+ return true;
+}
+
+template <typename Unit>
+bool ModuleCompiler<Unit>::compile(JSContext* maybeCx, FrontendContext* fc) {
+ // Emplace the topLevel stencil
+ MOZ_ASSERT(compilationState_.scriptData.length() ==
+ CompilationStencil::TopLevelIndex);
+ if (!compilationState_.appendScriptStencilAndData(fc)) {
+ return false;
+ }
+
+ ModuleBuilder builder(fc, parser.ptr());
+
+ const auto& options = compilationState_.input.options;
+
+ uint32_t len = this->sourceBuffer_.length();
+ SourceExtent extent =
+ SourceExtent::makeGlobalExtent(len, options.lineno, options.column);
+ ModuleSharedContext modulesc(fc, options, builder, extent);
+
+ ParseNode* pn = parser->moduleBody(&modulesc);
+ if (!pn) {
+ return false;
+ }
+
+ Maybe<BytecodeEmitter> emitter;
+ if (!emplaceEmitter(emitter, &modulesc)) {
+ return false;
+ }
+
+ if (!emitter->emitScript(pn->as<ModuleNode>().body())) {
+ return false;
+ }
+
+ StencilModuleMetadata& moduleMetadata = *compilationState_.moduleMetadata;
+
+ builder.finishFunctionDecls(moduleMetadata);
+
+ MOZ_ASSERT(!this->fc_->hadErrors());
+
+ return true;
+}
+
+// Parse a standalone JS function, which might appear as the value of an
+// event handler attribute in an HTML <INPUT> tag, or in a Function()
+// constructor.
+template <typename Unit>
+FunctionNode* StandaloneFunctionCompiler<Unit>::parse(
+ JSContext* cx, FunctionSyntaxKind syntaxKind, GeneratorKind generatorKind,
+ FunctionAsyncKind asyncKind, const Maybe<uint32_t>& parameterListEnd) {
+ assertSourceAndParserCreated();
+
+ TokenStreamPosition startPosition(parser->tokenStream);
+ auto startStatePosition = compilationState_.getPosition();
+
+ // Speculatively parse using the default directives implied by the context.
+ // If a directive is encountered (e.g., "use strict") that changes how the
+ // function should have been parsed, we backup and reparse with the new set
+ // of directives.
+
+ FunctionNode* fn;
+ for (;;) {
+ Directives newDirectives = compilationState_.directives;
+ fn = parser->standaloneFunction(parameterListEnd, syntaxKind, generatorKind,
+ asyncKind, compilationState_.directives,
+ &newDirectives);
+ if (fn) {
+ break;
+ }
+
+ // Maybe we encountered a new directive. See if we can try again.
+ if (!canHandleParseFailure(newDirectives)) {
+ return nullptr;
+ }
+
+ handleParseFailure(newDirectives, startPosition, startStatePosition);
+ }
+
+ return fn;
+}
+
+// Compile a standalone JS function.
+template <typename Unit>
+bool StandaloneFunctionCompiler<Unit>::compile(
+ JSContext* cx, FunctionSyntaxKind syntaxKind, GeneratorKind generatorKind,
+ FunctionAsyncKind asyncKind, const Maybe<uint32_t>& parameterListEnd) {
+ FunctionNode* parsedFunction =
+ parse(cx, syntaxKind, generatorKind, asyncKind, parameterListEnd);
+ if (!parsedFunction) {
+ return false;
+ }
+
+ FunctionBox* funbox = parsedFunction->funbox();
+
+ if (funbox->isInterpreted()) {
+ Maybe<BytecodeEmitter> emitter;
+ if (!emplaceEmitter(emitter, funbox)) {
+ return false;
+ }
+
+ if (!emitter->emitFunctionScript(parsedFunction)) {
+ return false;
+ }
+
+ // The parser extent has stripped off the leading `function...` but
+ // we want the SourceExtent used in the final standalone script to
+ // start from the beginning of the buffer, and use the provided
+ // line and column.
+ const auto& options = compilationState_.input.options;
+ compilationState_.scriptExtra[CompilationStencil::TopLevelIndex].extent =
+ SourceExtent{/* sourceStart = */ 0,
+ sourceBuffer_.length(),
+ funbox->extent().toStringStart,
+ funbox->extent().toStringEnd,
+ options.lineno,
+ options.column};
+ } else {
+ // The asm.js module was created by parser. Instantiation below will
+ // allocate the JSFunction that wraps it.
+ MOZ_ASSERT(funbox->isAsmJSModule());
+ MOZ_ASSERT(compilationState_.asmJS->moduleMap.has(funbox->index()));
+ MOZ_ASSERT(compilationState_.scriptData[CompilationStencil::TopLevelIndex]
+ .functionFlags.isAsmJSNative());
+ }
+
+ return true;
+}
+
+// Compile module, and return it as one of:
+// * ExtensibleCompilationStencil (without instantiation)
+// * CompilationStencil (without instantiation, has no external dependency)
+// * CompilationGCOutput (with instantiation).
+template <typename Unit>
+[[nodiscard]] static bool ParseModuleToStencilAndMaybeInstantiate(
+ JSContext* maybeCx, FrontendContext* fc, js::LifoAlloc& tempLifoAlloc,
+ CompilationInput& input, ScopeBindingCache* scopeCache,
+ SourceText<Unit>& srcBuf, BytecodeCompilerOutput& output) {
+ MOZ_ASSERT(srcBuf.get());
+
+ if (!input.initForModule(fc)) {
+ return false;
+ }
+
+ AutoAssertReportedException assertException(maybeCx, fc);
+
+ LifoAllocScope parserAllocScope(&tempLifoAlloc);
+ ModuleCompiler<Unit> compiler(fc, parserAllocScope, input, srcBuf);
+ if (!compiler.init(fc, scopeCache)) {
+ return false;
+ }
+
+ if (!compiler.compile(maybeCx, fc)) {
+ return false;
+ }
+
+ if (output.is<UniquePtr<ExtensibleCompilationStencil>>()) {
+ auto stencil =
+ fc->getAllocator()->make_unique<ExtensibleCompilationStencil>(
+ std::move(compiler.stencil()));
+ if (!stencil) {
+ return false;
+ }
+ output.as<UniquePtr<ExtensibleCompilationStencil>>() = std::move(stencil);
+ } else if (output.is<RefPtr<CompilationStencil>>()) {
+ Maybe<AutoGeckoProfilerEntry> pseudoFrame;
+ if (maybeCx) {
+ pseudoFrame.emplace(maybeCx, "script emit",
+ JS::ProfilingCategoryPair::JS_Parsing);
+ }
+
+ auto extensibleStencil =
+ fc->getAllocator()->make_unique<frontend::ExtensibleCompilationStencil>(
+ std::move(compiler.stencil()));
+ if (!extensibleStencil) {
+ return false;
+ }
+
+ RefPtr<CompilationStencil> stencil =
+ fc->getAllocator()->new_<CompilationStencil>(
+ std::move(extensibleStencil));
+ if (!stencil) {
+ return false;
+ }
+
+ output.as<RefPtr<CompilationStencil>>() = std::move(stencil);
+ } else {
+ MOZ_ASSERT(maybeCx);
+ BorrowingCompilationStencil borrowingStencil(compiler.stencil());
+ if (!InstantiateStencils(maybeCx, input, borrowingStencil,
+ *(output.as<CompilationGCOutput*>()))) {
+ return false;
+ }
+ }
+
+ assertException.reset();
+ return true;
+}
+
+template <typename Unit>
+already_AddRefed<CompilationStencil> ParseModuleToStencilImpl(
+ JSContext* maybeCx, FrontendContext* fc, js::LifoAlloc& tempLifoAlloc,
+ CompilationInput& input, ScopeBindingCache* scopeCache,
+ SourceText<Unit>& srcBuf) {
+ using OutputType = RefPtr<CompilationStencil>;
+ BytecodeCompilerOutput output((OutputType()));
+ if (!ParseModuleToStencilAndMaybeInstantiate(
+ maybeCx, fc, tempLifoAlloc, input, scopeCache, srcBuf, output)) {
+ return nullptr;
+ }
+ return output.as<OutputType>().forget();
+}
+
+already_AddRefed<CompilationStencil> frontend::ParseModuleToStencil(
+ JSContext* maybeCx, FrontendContext* fc, js::LifoAlloc& tempLifoAlloc,
+ CompilationInput& input, ScopeBindingCache* scopeCache,
+ SourceText<char16_t>& srcBuf) {
+ return ParseModuleToStencilImpl(maybeCx, fc, tempLifoAlloc, input, scopeCache,
+ srcBuf);
+}
+
+already_AddRefed<CompilationStencil> frontend::ParseModuleToStencil(
+ JSContext* maybeCx, FrontendContext* fc, js::LifoAlloc& tempLifoAlloc,
+ CompilationInput& input, ScopeBindingCache* scopeCache,
+ SourceText<Utf8Unit>& srcBuf) {
+ return ParseModuleToStencilImpl(maybeCx, fc, tempLifoAlloc, input, scopeCache,
+ srcBuf);
+}
+
+template <typename Unit>
+UniquePtr<ExtensibleCompilationStencil> ParseModuleToExtensibleStencilImpl(
+ JSContext* cx, FrontendContext* fc, js::LifoAlloc& tempLifoAlloc,
+ CompilationInput& input, ScopeBindingCache* scopeCache,
+ SourceText<Unit>& srcBuf) {
+ using OutputType = UniquePtr<ExtensibleCompilationStencil>;
+ BytecodeCompilerOutput output((OutputType()));
+ if (!ParseModuleToStencilAndMaybeInstantiate(cx, fc, tempLifoAlloc, input,
+ scopeCache, srcBuf, output)) {
+ return nullptr;
+ }
+ return std::move(output.as<OutputType>());
+}
+
+UniquePtr<ExtensibleCompilationStencil>
+frontend::ParseModuleToExtensibleStencil(JSContext* cx, FrontendContext* fc,
+ js::LifoAlloc& tempLifoAlloc,
+ CompilationInput& input,
+ ScopeBindingCache* scopeCache,
+ SourceText<char16_t>& srcBuf) {
+ return ParseModuleToExtensibleStencilImpl(cx, fc, tempLifoAlloc, input,
+ scopeCache, srcBuf);
+}
+
+UniquePtr<ExtensibleCompilationStencil>
+frontend::ParseModuleToExtensibleStencil(JSContext* cx, FrontendContext* fc,
+ js::LifoAlloc& tempLifoAlloc,
+ CompilationInput& input,
+ ScopeBindingCache* scopeCache,
+ SourceText<Utf8Unit>& srcBuf) {
+ return ParseModuleToExtensibleStencilImpl(cx, fc, tempLifoAlloc, input,
+ scopeCache, srcBuf);
+}
+
+template <typename Unit>
+static ModuleObject* CompileModuleImpl(
+ JSContext* cx, FrontendContext* fc,
+ const JS::ReadOnlyCompileOptions& optionsInput, SourceText<Unit>& srcBuf) {
+ AutoAssertReportedException assertException(cx, fc);
+
+ CompileOptions options(cx, optionsInput);
+ options.setModule();
+
+ Rooted<CompilationInput> input(cx, CompilationInput(options));
+ Rooted<CompilationGCOutput> gcOutput(cx);
+ BytecodeCompilerOutput output(gcOutput.address());
+
+ NoScopeBindingCache scopeCache;
+ if (!ParseModuleToStencilAndMaybeInstantiate(cx, fc, cx->tempLifoAlloc(),
+ input.get(), &scopeCache, srcBuf,
+ output)) {
+ return nullptr;
+ }
+
+ assertException.reset();
+ return gcOutput.get().module;
+}
+
+ModuleObject* frontend::CompileModule(JSContext* cx, FrontendContext* fc,
+ const JS::ReadOnlyCompileOptions& options,
+ SourceText<char16_t>& srcBuf) {
+ return CompileModuleImpl(cx, fc, options, srcBuf);
+}
+
+ModuleObject* frontend::CompileModule(JSContext* cx, FrontendContext* fc,
+ const JS::ReadOnlyCompileOptions& options,
+ SourceText<Utf8Unit>& srcBuf) {
+ return CompileModuleImpl(cx, fc, options, srcBuf);
+}
+
+static bool InstantiateLazyFunction(JSContext* cx, CompilationInput& input,
+ CompilationStencil& stencil,
+ BytecodeCompilerOutput& output) {
+ // We do check the type, but do not write anything to it as this is not
+ // necessary for lazy function, as the script is patched inside the
+ // JSFunction when instantiating.
+ MOZ_ASSERT(output.is<CompilationGCOutput*>());
+ MOZ_ASSERT(!output.as<CompilationGCOutput*>());
+
+ mozilla::DebugOnly<uint32_t> lazyFlags =
+ static_cast<uint32_t>(input.immutableFlags());
+
+ Rooted<CompilationGCOutput> gcOutput(cx);
+
+ if (input.source->hasEncoder()) {
+ if (!input.source->addDelazificationToIncrementalEncoding(cx, stencil)) {
+ return false;
+ }
+ }
+
+ if (!CompilationStencil::instantiateStencils(cx, input, stencil,
+ gcOutput.get())) {
+ return false;
+ }
+
+ // NOTE: After instantiation succeeds and bytecode is attached, the rest of
+ // this operation should be infallible. Any failure during
+ // delazification should restore the function back to a consistent
+ // lazy state.
+
+ MOZ_ASSERT(lazyFlags == gcOutput.get().script->immutableFlags());
+ MOZ_ASSERT(gcOutput.get().script->outermostScope()->hasOnChain(
+ ScopeKind::NonSyntactic) ==
+ gcOutput.get().script->immutableFlags().hasFlag(
+ JSScript::ImmutableFlags::HasNonSyntacticScope));
+
+ return true;
+}
+
+enum class GetCachedResult {
+ // Similar to return false.
+ Error,
+
+ // We have not found any entry.
+ NotFound,
+
+ // We have found an entry, and set everything according to the desired
+ // BytecodeCompilerOutput out-param.
+ Found
+};
+
+// When we have a cache hit, the addPtr out-param would evaluate to a true-ish
+// value.
+static GetCachedResult GetCachedLazyFunctionStencilMaybeInstantiate(
+ JSContext* cx, FrontendContext* fc, CompilationInput& input,
+ BytecodeCompilerOutput& output) {
+ RefPtr<CompilationStencil> stencil;
+ {
+ StencilCache& cache = cx->runtime()->caches().delazificationCache;
+ auto guard = cache.isSourceCached(input.source);
+ if (!guard) {
+ return GetCachedResult::NotFound;
+ }
+
+ // Before releasing the guard, which is locking the cache, we increment the
+ // reference counter such that we do not reclaim the CompilationStencil
+ // while we are instantiating it.
+ StencilContext key(input.source, input.extent());
+ stencil = cache.lookup(guard, key);
+ if (!stencil) {
+ return GetCachedResult::NotFound;
+ }
+ }
+
+ if (output.is<RefPtr<CompilationStencil>>()) {
+ output.as<RefPtr<CompilationStencil>>() = stencil;
+ return GetCachedResult::Found;
+ }
+
+ if (output.is<UniquePtr<ExtensibleCompilationStencil>>()) {
+ auto extensible =
+ fc->getAllocator()->make_unique<ExtensibleCompilationStencil>(input);
+ if (!extensible) {
+ return GetCachedResult::Error;
+ }
+ if (!extensible->cloneFrom(fc, *stencil)) {
+ return GetCachedResult::Error;
+ }
+
+ output.as<UniquePtr<ExtensibleCompilationStencil>>() =
+ std::move(extensible);
+ return GetCachedResult::Found;
+ }
+
+ if (!InstantiateLazyFunction(cx, input, *stencil, output)) {
+ return GetCachedResult::Error;
+ }
+
+ return GetCachedResult::Found;
+}
+
+template <typename Unit>
+static bool CompileLazyFunctionToStencilMaybeInstantiate(
+ JSContext* cx, FrontendContext* fc, CompilationInput& input,
+ ScopeBindingCache* scopeCache, const Unit* units, size_t length,
+ BytecodeCompilerOutput& output) {
+ MOZ_ASSERT(input.source);
+
+ AutoAssertReportedException assertException(cx, fc);
+ if (input.options.consumeDelazificationCache()) {
+ auto res =
+ GetCachedLazyFunctionStencilMaybeInstantiate(cx, fc, input, output);
+ switch (res) {
+ case GetCachedResult::Error:
+ return false;
+ case GetCachedResult::Found:
+ assertException.reset();
+ return true;
+ case GetCachedResult::NotFound:
+ break;
+ }
+ }
+
+ InheritThis inheritThis =
+ input.functionFlags().isArrow() ? InheritThis::Yes : InheritThis::No;
+
+ LifoAllocScope parserAllocScope(&cx->tempLifoAlloc());
+ CompilationState compilationState(fc, parserAllocScope, input);
+ compilationState.setFunctionKey(input.extent());
+ MOZ_ASSERT(!compilationState.isInitialStencil());
+ if (!compilationState.init(fc, scopeCache, inheritThis)) {
+ return false;
+ }
+
+ Parser<FullParseHandler, Unit> parser(fc, input.options, units, length,
+ /* foldConstants = */ true,
+ compilationState,
+ /* syntaxParser = */ nullptr);
+ if (!parser.checkOptions()) {
+ return false;
+ }
+
+ FunctionNode* pn = parser.standaloneLazyFunction(
+ input, input.extent().toStringStart, input.strict(),
+ input.generatorKind(), input.asyncKind());
+ if (!pn) {
+ return false;
+ }
+
+ BytecodeEmitter bce(fc, &parser, pn->funbox(), compilationState,
+ BytecodeEmitter::LazyFunction);
+ if (!bce.init(pn->pn_pos)) {
+ return false;
+ }
+
+ if (!bce.emitFunctionScript(pn)) {
+ return false;
+ }
+
+ // NOTE: Only allow relazification if there was no lazy PrivateScriptData.
+ // This excludes non-leaf functions and all script class constructors.
+ bool hadLazyScriptData = input.hasPrivateScriptData();
+ bool isRelazifiableAfterDelazify = input.isRelazifiable();
+ if (isRelazifiableAfterDelazify && !hadLazyScriptData) {
+ compilationState.scriptData[CompilationStencil::TopLevelIndex]
+ .setAllowRelazify();
+ }
+
+ if (input.options.checkDelazificationCache()) {
+ using OutputType = RefPtr<CompilationStencil>;
+ BytecodeCompilerOutput cached((OutputType()));
+ auto res =
+ GetCachedLazyFunctionStencilMaybeInstantiate(cx, fc, input, cached);
+ if (res == GetCachedResult::Error) {
+ return false;
+ }
+ // Cached results might be removed by GCs.
+ if (res == GetCachedResult::Found) {
+ auto& concurrentSharedData = cached.as<OutputType>().get()->sharedData;
+ auto concurrentData =
+ concurrentSharedData.isSingle()
+ ? concurrentSharedData.asSingle()->get()->immutableData()
+ : concurrentSharedData.asBorrow()
+ ->asSingle()
+ ->get()
+ ->immutableData();
+ auto ondemandData =
+ compilationState.sharedData.asSingle()->get()->immutableData();
+ MOZ_RELEASE_ASSERT(concurrentData.Length() == ondemandData.Length(),
+ "Non-deterministic stencils");
+ for (size_t i = 0; i < concurrentData.Length(); i++) {
+ MOZ_RELEASE_ASSERT(concurrentData[i] == ondemandData[i],
+ "Non-deterministic stencils");
+ }
+ }
+ }
+
+ if (output.is<UniquePtr<ExtensibleCompilationStencil>>()) {
+ auto stencil =
+ fc->getAllocator()->make_unique<ExtensibleCompilationStencil>(
+ std::move(compilationState));
+ if (!stencil) {
+ return false;
+ }
+ output.as<UniquePtr<ExtensibleCompilationStencil>>() = std::move(stencil);
+ } else if (output.is<RefPtr<CompilationStencil>>()) {
+ Maybe<AutoGeckoProfilerEntry> pseudoFrame;
+ if (cx) {
+ pseudoFrame.emplace(cx, "script emit",
+ JS::ProfilingCategoryPair::JS_Parsing);
+ }
+
+ auto extensibleStencil =
+ fc->getAllocator()->make_unique<frontend::ExtensibleCompilationStencil>(
+ std::move(compilationState));
+ if (!extensibleStencil) {
+ return false;
+ }
+
+ RefPtr<CompilationStencil> stencil =
+ fc->getAllocator()->new_<CompilationStencil>(
+ std::move(extensibleStencil));
+ if (!stencil) {
+ return false;
+ }
+
+ output.as<RefPtr<CompilationStencil>>() = std::move(stencil);
+ } else {
+ BorrowingCompilationStencil borrowingStencil(compilationState);
+ if (!InstantiateLazyFunction(cx, input, borrowingStencil, output)) {
+ return false;
+ }
+ }
+
+ assertException.reset();
+ return true;
+}
+
+template <typename Unit>
+static bool DelazifyCanonicalScriptedFunctionImpl(
+ JSContext* cx, FrontendContext* fc, ScopeBindingCache* scopeCache,
+ HandleFunction fun, Handle<BaseScript*> lazy, ScriptSource* ss) {
+ MOZ_ASSERT(!lazy->hasBytecode(), "Script is already compiled!");
+ MOZ_ASSERT(lazy->function() == fun);
+
+ MOZ_DIAGNOSTIC_ASSERT(!fun->isGhost());
+
+ AutoIncrementalTimer timer(cx->realm()->timers.delazificationTime);
+
+ size_t sourceStart = lazy->sourceStart();
+ size_t sourceLength = lazy->sourceEnd() - lazy->sourceStart();
+
+ MOZ_ASSERT(ss->hasSourceText());
+
+ // Parse and compile the script from source.
+ UncompressedSourceCache::AutoHoldEntry holder;
+
+ MOZ_ASSERT(ss->hasSourceType<Unit>());
+
+ ScriptSource::PinnedUnits<Unit> units(cx, ss, holder, sourceStart,
+ sourceLength);
+ if (!units.get()) {
+ return false;
+ }
+
+ JS::CompileOptions options(cx);
+ options.setMutedErrors(lazy->mutedErrors())
+ .setFileAndLine(lazy->filename(), lazy->lineno())
+ .setColumn(lazy->column())
+ .setScriptSourceOffset(lazy->sourceStart())
+ .setNoScriptRval(false)
+ .setSelfHostingMode(false)
+ .setEagerDelazificationStrategy(lazy->delazificationMode());
+
+ Rooted<CompilationInput> input(cx, CompilationInput(options));
+ input.get().initFromLazy(cx, lazy, ss);
+
+ CompilationGCOutput* unusedGcOutput = nullptr;
+ BytecodeCompilerOutput output(unusedGcOutput);
+ return CompileLazyFunctionToStencilMaybeInstantiate(
+ cx, fc, input.get(), scopeCache, units.get(), sourceLength, output);
+}
+
+bool frontend::DelazifyCanonicalScriptedFunction(JSContext* cx,
+ FrontendContext* fc,
+ HandleFunction fun) {
+ Maybe<AutoGeckoProfilerEntry> pseudoFrame;
+ if (cx) {
+ pseudoFrame.emplace(cx, "script delazify",
+ JS::ProfilingCategoryPair::JS_Parsing);
+ }
+
+ Rooted<BaseScript*> lazy(cx, fun->baseScript());
+ ScriptSource* ss = lazy->scriptSource();
+ ScopeBindingCache* scopeCache = &cx->caches().scopeCache;
+
+ if (ss->hasSourceType<Utf8Unit>()) {
+ // UTF-8 source text.
+ return DelazifyCanonicalScriptedFunctionImpl<Utf8Unit>(cx, fc, scopeCache,
+ fun, lazy, ss);
+ }
+
+ MOZ_ASSERT(ss->hasSourceType<char16_t>());
+
+ // UTF-16 source text.
+ return DelazifyCanonicalScriptedFunctionImpl<char16_t>(cx, fc, scopeCache,
+ fun, lazy, ss);
+}
+
+template <typename Unit>
+static already_AddRefed<CompilationStencil>
+DelazifyCanonicalScriptedFunctionImpl(JSContext* cx, FrontendContext* fc,
+ ScopeBindingCache* scopeCache,
+ CompilationStencil& context,
+ ScriptIndex scriptIndex) {
+ ScriptStencilRef script{context, scriptIndex};
+ const ScriptStencilExtra& extra = script.scriptExtra();
+
+#if defined(EARLY_BETA_OR_EARLIER) || defined(DEBUG)
+ const ScriptStencil& data = script.scriptData();
+ MOZ_ASSERT(!data.hasSharedData(), "Script is already compiled!");
+ MOZ_DIAGNOSTIC_ASSERT(!data.isGhost());
+#endif
+
+ Maybe<AutoIncrementalTimer> timer;
+ if (cx->realm()) {
+ timer.emplace(cx->realm()->timers.delazificationTime);
+ }
+
+ size_t sourceStart = extra.extent.sourceStart;
+ size_t sourceLength = extra.extent.sourceEnd - sourceStart;
+
+ ScriptSource* ss = context.source;
+ MOZ_ASSERT(ss->hasSourceText());
+
+ // Parse and compile the script from source.
+ UncompressedSourceCache::AutoHoldEntry holder;
+
+ MOZ_ASSERT(ss->hasSourceType<Unit>());
+
+ ScriptSource::PinnedUnits<Unit> units(cx, ss, holder, sourceStart,
+ sourceLength);
+ if (!units.get()) {
+ return nullptr;
+ }
+
+ JS::CompileOptions options(cx);
+ options.setMutedErrors(ss->mutedErrors())
+ .setFileAndLine(ss->filename(), extra.extent.lineno)
+ .setColumn(extra.extent.column)
+ .setScriptSourceOffset(sourceStart)
+ .setNoScriptRval(false)
+ .setSelfHostingMode(false);
+
+ Rooted<CompilationInput> input(cx, CompilationInput(options));
+ input.get().initFromStencil(context, scriptIndex, ss);
+
+ using OutputType = RefPtr<CompilationStencil>;
+ BytecodeCompilerOutput output((OutputType()));
+ if (!CompileLazyFunctionToStencilMaybeInstantiate(
+ cx, fc, input.get(), scopeCache, units.get(), sourceLength, output)) {
+ return nullptr;
+ }
+ return output.as<OutputType>().forget();
+}
+
+already_AddRefed<CompilationStencil>
+frontend::DelazifyCanonicalScriptedFunction(JSContext* cx, FrontendContext* fc,
+ ScopeBindingCache* scopeCache,
+ CompilationStencil& context,
+ ScriptIndex scriptIndex) {
+ Maybe<AutoGeckoProfilerEntry> pseudoFrame;
+ if (cx) {
+ pseudoFrame.emplace(cx, "stencil script delazify",
+ JS::ProfilingCategoryPair::JS_Parsing);
+ }
+
+ ScriptSource* ss = context.source;
+ if (ss->hasSourceType<Utf8Unit>()) {
+ // UTF-8 source text.
+ return DelazifyCanonicalScriptedFunctionImpl<Utf8Unit>(
+ cx, fc, scopeCache, context, scriptIndex);
+ }
+
+ // UTF-16 source text.
+ MOZ_ASSERT(ss->hasSourceType<char16_t>());
+ return DelazifyCanonicalScriptedFunctionImpl<char16_t>(cx, fc, scopeCache,
+ context, scriptIndex);
+}
+
+static JSFunction* CompileStandaloneFunction(
+ JSContext* cx, const JS::ReadOnlyCompileOptions& options,
+ JS::SourceText<char16_t>& srcBuf, const Maybe<uint32_t>& parameterListEnd,
+ FunctionSyntaxKind syntaxKind, GeneratorKind generatorKind,
+ FunctionAsyncKind asyncKind, Handle<Scope*> enclosingScope = nullptr) {
+ JS::Rooted<JSFunction*> fun(cx);
+ {
+ AutoReportFrontendContext fc(cx);
+ AutoAssertReportedException assertException(cx, &fc);
+
+ Rooted<CompilationInput> input(cx, CompilationInput(options));
+ if (enclosingScope) {
+ if (!input.get().initForStandaloneFunctionInNonSyntacticScope(
+ &fc, enclosingScope)) {
+ return nullptr;
+ }
+ } else {
+ if (!input.get().initForStandaloneFunction(cx, &fc)) {
+ return nullptr;
+ }
+ }
+
+ LifoAllocScope parserAllocScope(&cx->tempLifoAlloc());
+ InheritThis inheritThis = (syntaxKind == FunctionSyntaxKind::Arrow)
+ ? InheritThis::Yes
+ : InheritThis::No;
+ ScopeBindingCache* scopeCache = &cx->caches().scopeCache;
+ StandaloneFunctionCompiler<char16_t> compiler(&fc, parserAllocScope,
+ input.get(), srcBuf);
+ if (!compiler.init(&fc, scopeCache, inheritThis)) {
+ return nullptr;
+ }
+
+ if (!compiler.compile(cx, syntaxKind, generatorKind, asyncKind,
+ parameterListEnd)) {
+ return nullptr;
+ }
+
+ Rooted<CompilationGCOutput> gcOutput(cx);
+ RefPtr<ScriptSource> source;
+ {
+ BorrowingCompilationStencil borrowingStencil(compiler.stencil());
+ if (!CompilationStencil::instantiateStencils(
+ cx, input.get(), borrowingStencil, gcOutput.get())) {
+ return nullptr;
+ }
+ source = borrowingStencil.source;
+ }
+
+ fun = gcOutput.get().getFunctionNoBaseIndex(
+ CompilationStencil::TopLevelIndex);
+ MOZ_ASSERT(fun->hasBytecode() || IsAsmJSModule(fun));
+
+ // Enqueue an off-thread source compression task after finishing parsing.
+ if (!cx->isHelperThreadContext()) {
+ if (!source->tryCompressOffThread(cx)) {
+ return nullptr;
+ }
+ }
+
+ // Note: If AsmJS successfully compiles, the into.script will still be
+ // nullptr. In this case we have compiled to a native function instead of an
+ // interpreted script.
+ if (gcOutput.get().script) {
+ if (parameterListEnd) {
+ source->setParameterListEnd(*parameterListEnd);
+ }
+
+ MOZ_ASSERT(!cx->isHelperThreadContext());
+
+ const JS::InstantiateOptions instantiateOptions(options);
+ Rooted<JSScript*> script(cx, gcOutput.get().script);
+ FireOnNewScript(cx, instantiateOptions, script);
+ }
+
+ assertException.reset();
+ }
+ return fun;
+}
+
+JSFunction* frontend::CompileStandaloneFunction(
+ JSContext* cx, const JS::ReadOnlyCompileOptions& options,
+ JS::SourceText<char16_t>& srcBuf, const Maybe<uint32_t>& parameterListEnd,
+ FunctionSyntaxKind syntaxKind) {
+ return CompileStandaloneFunction(cx, options, srcBuf, parameterListEnd,
+ syntaxKind, GeneratorKind::NotGenerator,
+ FunctionAsyncKind::SyncFunction);
+}
+
+JSFunction* frontend::CompileStandaloneGenerator(
+ JSContext* cx, const JS::ReadOnlyCompileOptions& options,
+ JS::SourceText<char16_t>& srcBuf, const Maybe<uint32_t>& parameterListEnd,
+ FunctionSyntaxKind syntaxKind) {
+ return CompileStandaloneFunction(cx, options, srcBuf, parameterListEnd,
+ syntaxKind, GeneratorKind::Generator,
+ FunctionAsyncKind::SyncFunction);
+}
+
+JSFunction* frontend::CompileStandaloneAsyncFunction(
+ JSContext* cx, const ReadOnlyCompileOptions& options,
+ JS::SourceText<char16_t>& srcBuf, const Maybe<uint32_t>& parameterListEnd,
+ FunctionSyntaxKind syntaxKind) {
+ return CompileStandaloneFunction(cx, options, srcBuf, parameterListEnd,
+ syntaxKind, GeneratorKind::NotGenerator,
+ FunctionAsyncKind::AsyncFunction);
+}
+
+JSFunction* frontend::CompileStandaloneAsyncGenerator(
+ JSContext* cx, const ReadOnlyCompileOptions& options,
+ JS::SourceText<char16_t>& srcBuf, const Maybe<uint32_t>& parameterListEnd,
+ FunctionSyntaxKind syntaxKind) {
+ return CompileStandaloneFunction(cx, options, srcBuf, parameterListEnd,
+ syntaxKind, GeneratorKind::Generator,
+ FunctionAsyncKind::AsyncFunction);
+}
+
+JSFunction* frontend::CompileStandaloneFunctionInNonSyntacticScope(
+ JSContext* cx, const JS::ReadOnlyCompileOptions& options,
+ JS::SourceText<char16_t>& srcBuf, const Maybe<uint32_t>& parameterListEnd,
+ FunctionSyntaxKind syntaxKind, Handle<Scope*> enclosingScope) {
+ MOZ_ASSERT(enclosingScope);
+ return CompileStandaloneFunction(cx, options, srcBuf, parameterListEnd,
+ syntaxKind, GeneratorKind::NotGenerator,
+ FunctionAsyncKind::SyncFunction,
+ enclosingScope);
+}
+
+void frontend::FireOnNewScript(JSContext* cx,
+ const JS::InstantiateOptions& options,
+ JS::Handle<JSScript*> script) {
+ if (!options.hideFromNewScriptInitial()) {
+ DebugAPI::onNewScript(cx, script);
+ }
+}