Bug 1909211 - Bookmark dialogs suggests all already entered tags instead of just...
[gecko.git] / js / src / frontend / BytecodeCompiler.cpp
blob9ecf8fb2d35b7f904effe66398bc034a9b46438f
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "frontend/BytecodeCompiler.h"
9 #include "mozilla/Attributes.h"
10 #include "mozilla/Maybe.h"
11 #include "mozilla/Utf8.h" // mozilla::Utf8Unit
12 #include "mozilla/Variant.h" // mozilla::Variant
14 #include "debugger/DebugAPI.h"
15 #include "ds/LifoAlloc.h"
16 #include "frontend/BytecodeEmitter.h"
17 #include "frontend/CompilationStencil.h" // ExtensibleCompilationStencil, ExtraBindingInfoVector, CompilationInput, CompilationGCOutput
18 #include "frontend/EitherParser.h"
19 #ifdef JS_ENABLE_SMOOSH
20 # include "frontend/Frontend2.h" // Smoosh
21 #endif
22 #include "frontend/FrontendContext.h" // AutoReportFrontendContext
23 #include "frontend/ModuleSharedContext.h"
24 #include "frontend/ParserAtom.h" // ParserAtomsTable, TaggedParserAtomIndex
25 #include "frontend/SharedContext.h" // SharedContext, GlobalSharedContext
26 #include "frontend/Stencil.h" // ParserBindingIter
27 #include "frontend/UsedNameTracker.h" // UsedNameTracker, UsedNameMap
28 #include "js/AllocPolicy.h" // js::SystemAllocPolicy, ReportOutOfMemory
29 #include "js/CharacterEncoding.h" // JS_EncodeStringToUTF8
30 #include "js/ColumnNumber.h" // JS::ColumnNumberOneOrigin
31 #include "js/ErrorReport.h" // JS_ReportErrorASCII
32 #include "js/experimental/JSStencil.h"
33 #include "js/GCVector.h" // JS::StackGCVector
34 #include "js/Id.h" // JS::PropertyKey
35 #include "js/Modules.h" // JS::ImportAssertionVector
36 #include "js/RootingAPI.h" // JS::Handle, JS::MutableHandle
37 #include "js/SourceText.h" // JS::SourceText
38 #include "js/UniquePtr.h"
39 #include "js/Utility.h" // UniqueChars
40 #include "js/Value.h" // JS::Value
41 #include "vm/EnvironmentObject.h" // WithEnvironmentObject
42 #include "vm/FunctionFlags.h" // FunctionFlags
43 #include "vm/GeneratorAndAsyncKind.h" // js::GeneratorKind, js::FunctionAsyncKind
44 #include "vm/HelperThreads.h" // StartOffThreadDelazification, WaitForAllDelazifyTasks
45 #include "vm/JSContext.h" // JSContext
46 #include "vm/JSObject.h" // SetIntegrityLevel, IntegrityLevel
47 #include "vm/JSScript.h" // ScriptSource, UncompressedSourceCache
48 #include "vm/ModuleBuilder.h" // js::ModuleBuilder
49 #include "vm/NativeObject.h" // NativeDefineDataProperty
50 #include "vm/PlainObject.h" // NewPlainObjectWithProto
51 #include "vm/StencilCache.h" // DelazificationCache
52 #include "vm/Time.h" // AutoIncrementalTimer
53 #include "wasm/AsmJS.h"
55 #include "vm/Compartment-inl.h" // JS::Compartment::wrap
56 #include "vm/GeckoProfiler-inl.h"
57 #include "vm/JSContext-inl.h"
58 #include "vm/JSObject-inl.h" // JSObject::maybeHasInterestingSymbolProperty for ObjectOperations-inl.h
59 #include "vm/ObjectOperations-inl.h" // HasProperty
61 using namespace js;
62 using namespace js::frontend;
64 using mozilla::Maybe;
65 using mozilla::Utf8Unit;
67 using JS::CompileOptions;
68 using JS::ReadOnlyCompileOptions;
69 using JS::SourceText;
71 // RAII class to check the frontend reports an exception when it fails to
72 // compile a script.
73 class MOZ_RAII AutoAssertReportedException {
74 #ifdef DEBUG
75 JSContext* maybeCx_;
76 FrontendContext* fc_;
77 bool check_;
79 public:
80 explicit AutoAssertReportedException(JSContext* maybeCx, FrontendContext* fc)
81 : maybeCx_(maybeCx), fc_(fc), check_(true) {}
82 void reset() { check_ = false; }
83 ~AutoAssertReportedException() {
84 if (!check_) {
85 return;
88 // Error while compiling self-hosted code isn't set as an exception.
89 // TODO: Remove this once all errors are added to frontend context.
90 if (maybeCx_ && !maybeCx_->runtime()->hasInitializedSelfHosting()) {
91 return;
94 // TODO: Remove this once JSContext is removed from frontend.
95 if (maybeCx_) {
96 MOZ_ASSERT(maybeCx_->isExceptionPending() || fc_->hadErrors());
97 } else {
98 MOZ_ASSERT(fc_->hadErrors());
101 #else
102 public:
103 explicit AutoAssertReportedException(JSContext*, FrontendContext*) {}
104 void reset() {}
105 #endif
108 static bool EmplaceEmitter(CompilationState& compilationState,
109 Maybe<BytecodeEmitter>& emitter, FrontendContext* fc,
110 const EitherParser& parser, SharedContext* sc);
112 template <typename Unit>
113 class MOZ_STACK_CLASS SourceAwareCompiler {
114 protected:
115 SourceText<Unit>& sourceBuffer_;
117 CompilationState compilationState_;
119 Maybe<Parser<SyntaxParseHandler, Unit>> syntaxParser;
120 Maybe<Parser<FullParseHandler, Unit>> parser;
121 FrontendContext* fc_ = nullptr;
123 using TokenStreamPosition = frontend::TokenStreamPosition<Unit>;
125 protected:
126 explicit SourceAwareCompiler(FrontendContext* fc,
127 LifoAllocScope& parserAllocScope,
128 CompilationInput& input,
129 SourceText<Unit>& sourceBuffer)
130 : sourceBuffer_(sourceBuffer),
131 compilationState_(fc, parserAllocScope, input) {
132 MOZ_ASSERT(sourceBuffer_.get() != nullptr);
135 [[nodiscard]] bool init(FrontendContext* fc, ScopeBindingCache* scopeCache,
136 InheritThis inheritThis = InheritThis::No,
137 JSObject* enclosingEnv = nullptr) {
138 if (!compilationState_.init(fc, scopeCache, inheritThis, enclosingEnv)) {
139 return false;
142 return createSourceAndParser(fc);
145 // Call this before calling compile{Global,Eval}Script.
146 [[nodiscard]] bool createSourceAndParser(FrontendContext* fc);
148 void assertSourceAndParserCreated() const {
149 MOZ_ASSERT(compilationState_.source != nullptr);
150 MOZ_ASSERT(parser.isSome());
153 void assertSourceParserAndScriptCreated() { assertSourceAndParserCreated(); }
155 [[nodiscard]] bool emplaceEmitter(Maybe<BytecodeEmitter>& emitter,
156 SharedContext* sharedContext) {
157 return EmplaceEmitter(compilationState_, emitter, fc_,
158 EitherParser(parser.ptr()), sharedContext);
161 bool canHandleParseFailure(const Directives& newDirectives);
163 void handleParseFailure(
164 const Directives& newDirectives, TokenStreamPosition& startPosition,
165 CompilationState::CompilationStatePosition& startStatePosition);
167 public:
168 CompilationState& compilationState() { return compilationState_; };
170 ExtensibleCompilationStencil& stencil() { return compilationState_; }
173 template <typename Unit>
174 class MOZ_STACK_CLASS ScriptCompiler : public SourceAwareCompiler<Unit> {
175 using Base = SourceAwareCompiler<Unit>;
177 protected:
178 using Base::compilationState_;
179 using Base::parser;
180 using Base::sourceBuffer_;
182 using Base::assertSourceParserAndScriptCreated;
183 using Base::canHandleParseFailure;
184 using Base::emplaceEmitter;
185 using Base::handleParseFailure;
187 using typename Base::TokenStreamPosition;
189 public:
190 explicit ScriptCompiler(FrontendContext* fc, LifoAllocScope& parserAllocScope,
191 CompilationInput& input,
192 SourceText<Unit>& sourceBuffer)
193 : Base(fc, parserAllocScope, input, sourceBuffer) {}
195 using Base::init;
196 using Base::stencil;
198 [[nodiscard]] bool compile(JSContext* cx, SharedContext* sc);
200 private:
201 [[nodiscard]] bool popupateExtraBindingsFields(GlobalSharedContext* globalsc);
204 #ifdef JS_ENABLE_SMOOSH
205 [[nodiscard]] static bool TrySmoosh(
206 JSContext* cx, FrontendContext* fc, CompilationInput& input,
207 JS::SourceText<mozilla::Utf8Unit>& srcBuf,
208 UniquePtr<ExtensibleCompilationStencil>& stencilOut) {
209 MOZ_ASSERT(!stencilOut);
211 if (!cx->options().trySmoosh()) {
212 return true;
215 JSRuntime* rt = cx->runtime();
216 if (!Smoosh::tryCompileGlobalScriptToExtensibleStencil(cx, fc, input, srcBuf,
217 stencilOut)) {
218 return false;
221 if (cx->options().trackNotImplemented()) {
222 if (stencilOut) {
223 rt->parserWatcherFile.put("1");
224 } else {
225 rt->parserWatcherFile.put("0");
229 if (!stencilOut) {
230 fprintf(stderr, "Falling back!\n");
231 return true;
234 return stencilOut->source->assignSource(fc, input.options, srcBuf);
237 [[nodiscard]] static bool TrySmoosh(
238 JSContext* cx, FrontendContext* fc, CompilationInput& input,
239 JS::SourceText<char16_t>& srcBuf,
240 UniquePtr<ExtensibleCompilationStencil>& stencilOut) {
241 MOZ_ASSERT(!stencilOut);
242 return true;
244 #endif // JS_ENABLE_SMOOSH
246 using BytecodeCompilerOutput =
247 mozilla::Variant<UniquePtr<ExtensibleCompilationStencil>,
248 RefPtr<CompilationStencil>, CompilationGCOutput*>;
250 static constexpr ExtraBindingInfoVector* NoExtraBindings = nullptr;
252 // Compile global script, and return it as one of:
253 // * ExtensibleCompilationStencil (without instantiation)
254 // * CompilationStencil (without instantiation, has no external dependency)
255 // * CompilationGCOutput (with instantiation).
256 template <typename Unit>
257 [[nodiscard]] static bool CompileGlobalScriptToStencilAndMaybeInstantiate(
258 JSContext* maybeCx, FrontendContext* fc, js::LifoAlloc& tempLifoAlloc,
259 CompilationInput& input, ScopeBindingCache* scopeCache,
260 JS::SourceText<Unit>& srcBuf, ScopeKind scopeKind,
261 ExtraBindingInfoVector* maybeExtraBindings,
262 BytecodeCompilerOutput& output) {
263 #ifdef JS_ENABLE_SMOOSH
264 if (maybeCx) {
265 UniquePtr<ExtensibleCompilationStencil> extensibleStencil;
266 if (!TrySmoosh(maybeCx, fc, input, srcBuf, extensibleStencil)) {
267 return false;
269 if (extensibleStencil) {
270 if (input.options.populateDelazificationCache()) {
271 BorrowingCompilationStencil borrowingStencil(*extensibleStencil);
272 StartOffThreadDelazification(maybeCx, input.options, borrowingStencil);
274 // When we are trying to validate whether on-demand delazification
275 // generate the same stencil as concurrent delazification, we want to
276 // parse everything eagerly off-thread ahead of re-parsing everything on
277 // demand, to compare the outcome.
278 if (input.options.waitForDelazificationCache()) {
279 WaitForAllDelazifyTasks(maybeCx->runtime());
282 if (output.is<UniquePtr<ExtensibleCompilationStencil>>()) {
283 output.as<UniquePtr<ExtensibleCompilationStencil>>() =
284 std::move(extensibleStencil);
285 } else if (output.is<RefPtr<CompilationStencil>>()) {
286 RefPtr<CompilationStencil> stencil =
287 fc->getAllocator()->new_<frontend::CompilationStencil>(
288 std::move(extensibleStencil));
289 if (!stencil) {
290 return false;
293 output.as<RefPtr<CompilationStencil>>() = std::move(stencil);
294 } else {
295 BorrowingCompilationStencil borrowingStencil(*extensibleStencil);
296 if (!InstantiateStencils(maybeCx, input, borrowingStencil,
297 *(output.as<CompilationGCOutput*>()))) {
298 return false;
301 return true;
304 #endif // JS_ENABLE_SMOOSH
306 if (input.options.selfHostingMode) {
307 if (!input.initForSelfHostingGlobal(fc)) {
308 return false;
310 } else if (maybeExtraBindings) {
311 if (!input.initForGlobalWithExtraBindings(fc, maybeExtraBindings)) {
312 return false;
314 } else {
315 if (!input.initForGlobal(fc)) {
316 return false;
320 AutoAssertReportedException assertException(maybeCx, fc);
322 LifoAllocScope parserAllocScope(&tempLifoAlloc);
323 ScriptCompiler<Unit> compiler(fc, parserAllocScope, input, srcBuf);
324 if (!compiler.init(fc, scopeCache)) {
325 return false;
328 SourceExtent extent = SourceExtent::makeGlobalExtent(
329 srcBuf.length(), input.options.lineno,
330 JS::LimitedColumnNumberOneOrigin::fromUnlimited(
331 JS::ColumnNumberOneOrigin(input.options.column)));
333 GlobalSharedContext globalsc(fc, scopeKind, input.options,
334 compiler.compilationState().directives, extent);
336 if (!compiler.compile(maybeCx, &globalsc)) {
337 return false;
340 if (input.options.populateDelazificationCache()) {
341 // NOTE: Delazification can be triggered from off-thread compilation.
342 BorrowingCompilationStencil borrowingStencil(compiler.stencil());
343 StartOffThreadDelazification(maybeCx, input.options, borrowingStencil);
345 // When we are trying to validate whether on-demand delazification
346 // generate the same stencil as concurrent delazification, we want to
347 // parse everything eagerly off-thread ahead of re-parsing everything on
348 // demand, to compare the outcome.
350 // This option works only from main-thread compilation, to avoid
351 // dead-lock.
352 if (input.options.waitForDelazificationCache() && maybeCx) {
353 WaitForAllDelazifyTasks(maybeCx->runtime());
357 if (output.is<UniquePtr<ExtensibleCompilationStencil>>()) {
358 auto stencil =
359 fc->getAllocator()->make_unique<ExtensibleCompilationStencil>(
360 std::move(compiler.stencil()));
361 if (!stencil) {
362 return false;
364 output.as<UniquePtr<ExtensibleCompilationStencil>>() = std::move(stencil);
365 } else if (output.is<RefPtr<CompilationStencil>>()) {
366 Maybe<AutoGeckoProfilerEntry> pseudoFrame;
367 if (maybeCx) {
368 pseudoFrame.emplace(maybeCx, "script emit",
369 JS::ProfilingCategoryPair::JS_Parsing);
372 auto extensibleStencil =
373 fc->getAllocator()->make_unique<frontend::ExtensibleCompilationStencil>(
374 std::move(compiler.stencil()));
375 if (!extensibleStencil) {
376 return false;
379 RefPtr<CompilationStencil> stencil =
380 fc->getAllocator()->new_<CompilationStencil>(
381 std::move(extensibleStencil));
382 if (!stencil) {
383 return false;
386 output.as<RefPtr<CompilationStencil>>() = std::move(stencil);
387 } else {
388 MOZ_ASSERT(maybeCx);
389 BorrowingCompilationStencil borrowingStencil(compiler.stencil());
390 if (!InstantiateStencils(maybeCx, input, borrowingStencil,
391 *(output.as<CompilationGCOutput*>()))) {
392 return false;
396 assertException.reset();
397 return true;
400 template <typename Unit>
401 static already_AddRefed<CompilationStencil> CompileGlobalScriptToStencilImpl(
402 JSContext* maybeCx, FrontendContext* fc, js::LifoAlloc& tempLifoAlloc,
403 CompilationInput& input, ScopeBindingCache* scopeCache,
404 JS::SourceText<Unit>& srcBuf, ScopeKind scopeKind) {
405 using OutputType = RefPtr<CompilationStencil>;
406 BytecodeCompilerOutput output((OutputType()));
407 if (!CompileGlobalScriptToStencilAndMaybeInstantiate(
408 maybeCx, fc, tempLifoAlloc, input, scopeCache, srcBuf, scopeKind,
409 NoExtraBindings, output)) {
410 return nullptr;
412 return output.as<OutputType>().forget();
415 already_AddRefed<CompilationStencil> frontend::CompileGlobalScriptToStencil(
416 JSContext* cx, FrontendContext* fc, js::LifoAlloc& tempLifoAlloc,
417 CompilationInput& input, ScopeBindingCache* scopeCache,
418 JS::SourceText<char16_t>& srcBuf, ScopeKind scopeKind) {
419 return CompileGlobalScriptToStencilImpl(cx, fc, tempLifoAlloc, input,
420 scopeCache, srcBuf, scopeKind);
423 already_AddRefed<CompilationStencil> frontend::CompileGlobalScriptToStencil(
424 JSContext* cx, FrontendContext* fc, js::LifoAlloc& tempLifoAlloc,
425 CompilationInput& input, ScopeBindingCache* scopeCache,
426 JS::SourceText<Utf8Unit>& srcBuf, ScopeKind scopeKind) {
427 return CompileGlobalScriptToStencilImpl(cx, fc, tempLifoAlloc, input,
428 scopeCache, srcBuf, scopeKind);
431 template <typename Unit>
432 static UniquePtr<ExtensibleCompilationStencil>
433 CompileGlobalScriptToExtensibleStencilImpl(JSContext* maybeCx,
434 FrontendContext* fc,
435 CompilationInput& input,
436 ScopeBindingCache* scopeCache,
437 JS::SourceText<Unit>& srcBuf,
438 ScopeKind scopeKind) {
439 using OutputType = UniquePtr<ExtensibleCompilationStencil>;
440 BytecodeCompilerOutput output((OutputType()));
441 if (!CompileGlobalScriptToStencilAndMaybeInstantiate(
442 maybeCx, fc, maybeCx->tempLifoAlloc(), input, scopeCache, srcBuf,
443 scopeKind, NoExtraBindings, output)) {
444 return nullptr;
446 return std::move(output.as<OutputType>());
449 UniquePtr<ExtensibleCompilationStencil>
450 frontend::CompileGlobalScriptToExtensibleStencil(
451 JSContext* maybeCx, FrontendContext* fc, CompilationInput& input,
452 ScopeBindingCache* scopeCache, JS::SourceText<char16_t>& srcBuf,
453 ScopeKind scopeKind) {
454 return CompileGlobalScriptToExtensibleStencilImpl(
455 maybeCx, fc, input, scopeCache, srcBuf, scopeKind);
458 UniquePtr<ExtensibleCompilationStencil>
459 frontend::CompileGlobalScriptToExtensibleStencil(
460 JSContext* cx, FrontendContext* fc, CompilationInput& input,
461 ScopeBindingCache* scopeCache, JS::SourceText<Utf8Unit>& srcBuf,
462 ScopeKind scopeKind) {
463 return CompileGlobalScriptToExtensibleStencilImpl(cx, fc, input, scopeCache,
464 srcBuf, scopeKind);
467 static void FireOnNewScript(JSContext* cx,
468 const JS::InstantiateOptions& options,
469 JS::Handle<JSScript*> script) {
470 if (!options.hideFromNewScriptInitial()) {
471 DebugAPI::onNewScript(cx, script);
475 bool frontend::InstantiateStencils(JSContext* cx, CompilationInput& input,
476 const CompilationStencil& stencil,
477 CompilationGCOutput& gcOutput) {
479 AutoGeckoProfilerEntry pseudoFrame(cx, "stencil instantiate",
480 JS::ProfilingCategoryPair::JS_Parsing);
482 if (!CompilationStencil::instantiateStencils(cx, input, stencil,
483 gcOutput)) {
484 return false;
488 // Enqueue an off-thread source compression task after finishing parsing.
489 if (!stencil.source->tryCompressOffThread(cx)) {
490 return false;
493 Rooted<JSScript*> script(cx, gcOutput.script);
494 const JS::InstantiateOptions instantiateOptions(input.options);
495 FireOnNewScript(cx, instantiateOptions, script);
497 return true;
500 template <typename Unit>
501 static JSScript* CompileGlobalScriptImpl(
502 JSContext* cx, FrontendContext* fc,
503 const JS::ReadOnlyCompileOptions& options, JS::SourceText<Unit>& srcBuf,
504 ScopeKind scopeKind, ExtraBindingInfoVector* maybeExtraBindings) {
505 Rooted<CompilationInput> input(cx, CompilationInput(options));
506 Rooted<CompilationGCOutput> gcOutput(cx);
507 BytecodeCompilerOutput output(gcOutput.address());
508 NoScopeBindingCache scopeCache;
509 if (!CompileGlobalScriptToStencilAndMaybeInstantiate(
510 cx, fc, cx->tempLifoAlloc(), input.get(), &scopeCache, srcBuf,
511 scopeKind, maybeExtraBindings, output)) {
512 return nullptr;
514 return gcOutput.get().script;
517 JSScript* frontend::CompileGlobalScript(
518 JSContext* cx, FrontendContext* fc,
519 const JS::ReadOnlyCompileOptions& options, JS::SourceText<char16_t>& srcBuf,
520 ScopeKind scopeKind) {
521 return CompileGlobalScriptImpl(cx, fc, options, srcBuf, scopeKind,
522 NoExtraBindings);
525 static bool CreateExtraBindingInfoVector(
526 JSContext* cx,
527 JS::Handle<JS::StackGCVector<JS::PropertyKey>> unwrappedBindingKeys,
528 JS::Handle<JS::StackGCVector<JS::Value>> unwrappedBindingValues,
529 ExtraBindingInfoVector& extraBindings) {
530 MOZ_ASSERT(unwrappedBindingKeys.length() == unwrappedBindingValues.length());
532 if (!extraBindings.reserve(unwrappedBindingKeys.length())) {
533 ReportOutOfMemory(cx);
534 return false;
537 JS::Rooted<JSObject*> globalLexical(cx, &cx->global()->lexicalEnvironment());
538 JS::Rooted<JS::PropertyKey> id(cx);
539 for (size_t i = 0; i < unwrappedBindingKeys.length(); i++) {
540 if (!unwrappedBindingKeys[i].isString()) {
541 JS_ReportErrorASCII(cx, "The bindings key should be a string.");
542 return false;
545 JS::Rooted<JSString*> str(cx, unwrappedBindingKeys[i].toString());
547 UniqueChars utf8chars = JS_EncodeStringToUTF8(cx, str);
548 if (!utf8chars) {
549 return false;
552 bool isShadowed = false;
554 id = unwrappedBindingKeys[i];
555 cx->markId(id);
557 bool found;
558 if (!HasProperty(cx, cx->global(), id, &found)) {
559 return false;
561 if (found) {
562 isShadowed = true;
563 } else {
564 if (!HasProperty(cx, globalLexical, id, &found)) {
565 return false;
567 if (found) {
568 isShadowed = true;
572 extraBindings.infallibleEmplaceBack(std::move(utf8chars), isShadowed);
575 return true;
578 static WithEnvironmentObject* CreateExtraBindingsEnvironment(
579 JSContext* cx,
580 JS::Handle<JS::StackGCVector<JS::PropertyKey>> unwrappedBindingKeys,
581 JS::Handle<JS::StackGCVector<JS::Value>> unwrappedBindingValues,
582 const ExtraBindingInfoVector& extraBindings) {
583 JS::Rooted<PlainObject*> extraBindingsObj(
584 cx, NewPlainObjectWithProto(cx, nullptr));
585 if (!extraBindingsObj) {
586 return nullptr;
589 MOZ_ASSERT(unwrappedBindingKeys.length() == extraBindings.length());
591 JS::Rooted<JS::PropertyKey> id(cx);
592 size_t i = 0;
593 for (const auto& bindingInfo : extraBindings) {
594 if (bindingInfo.isShadowed) {
595 i++;
596 continue;
599 id = unwrappedBindingKeys[i];
600 cx->markId(id);
601 JS::Rooted<JS::Value> val(cx, unwrappedBindingValues[i]);
602 if (!cx->compartment()->wrap(cx, &val) ||
603 !NativeDefineDataProperty(cx, extraBindingsObj, id, val, 0)) {
604 return nullptr;
606 i++;
609 // The list of bindings shouldn't be modified.
610 if (!SetIntegrityLevel(cx, extraBindingsObj, IntegrityLevel::Sealed)) {
611 return nullptr;
614 JS::Rooted<JSObject*> globalLexical(cx, &cx->global()->lexicalEnvironment());
615 return WithEnvironmentObject::createNonSyntactic(cx, extraBindingsObj,
616 globalLexical);
619 JSScript* frontend::CompileGlobalScriptWithExtraBindings(
620 JSContext* cx, FrontendContext* fc,
621 const JS::ReadOnlyCompileOptions& options, JS::SourceText<char16_t>& srcBuf,
622 JS::Handle<JS::StackGCVector<JS::PropertyKey>> unwrappedBindingKeys,
623 JS::Handle<JS::StackGCVector<JS::Value>> unwrappedBindingValues,
624 JS::MutableHandle<JSObject*> env) {
625 ExtraBindingInfoVector extraBindings;
626 if (!CreateExtraBindingInfoVector(cx, unwrappedBindingKeys,
627 unwrappedBindingValues, extraBindings)) {
628 return nullptr;
631 JS::Rooted<JSScript*> script(
632 cx, CompileGlobalScriptImpl(cx, fc, options, srcBuf,
633 ScopeKind::NonSyntactic, &extraBindings));
634 if (!script) {
635 if (fc->extraBindingsAreNotUsed()) {
636 // Compile the script as regular global script in global lexical.
638 fc->clearNoExtraBindingReferencesFound();
640 // Warnings can be reported. Clear them to avoid reporting twice.
641 fc->clearWarnings();
643 // No other error should be reported.
644 MOZ_ASSERT(!fc->hadErrors());
645 MOZ_ASSERT(!cx->isExceptionPending());
647 env.set(&cx->global()->lexicalEnvironment());
649 JS::CompileOptions copiedOptions(nullptr, options);
650 copiedOptions.setNonSyntacticScope(false);
652 return CompileGlobalScript(cx, fc, copiedOptions, srcBuf,
653 ScopeKind::Global);
656 return nullptr;
659 WithEnvironmentObject* extraBindingsEnv = CreateExtraBindingsEnvironment(
660 cx, unwrappedBindingKeys, unwrappedBindingValues, extraBindings);
661 if (!extraBindingsEnv) {
662 return nullptr;
665 env.set(extraBindingsEnv);
667 return script;
670 JSScript* frontend::CompileGlobalScript(
671 JSContext* cx, FrontendContext* fc,
672 const JS::ReadOnlyCompileOptions& options, JS::SourceText<Utf8Unit>& srcBuf,
673 ScopeKind scopeKind) {
674 return CompileGlobalScriptImpl(cx, fc, options, srcBuf, scopeKind,
675 NoExtraBindings);
678 template <typename Unit>
679 static JSScript* CompileEvalScriptImpl(
680 JSContext* cx, const JS::ReadOnlyCompileOptions& options,
681 SourceText<Unit>& srcBuf, JS::Handle<js::Scope*> enclosingScope,
682 JS::Handle<JSObject*> enclosingEnv) {
683 JS::Rooted<JSScript*> script(cx);
685 AutoReportFrontendContext fc(cx);
686 AutoAssertReportedException assertException(cx, &fc);
688 Rooted<CompilationInput> input(cx, CompilationInput(options));
689 if (!input.get().initForEval(&fc, enclosingScope)) {
690 return nullptr;
693 LifoAllocScope parserAllocScope(&cx->tempLifoAlloc());
695 ScopeBindingCache* scopeCache = &cx->caches().scopeCache;
696 ScriptCompiler<Unit> compiler(&fc, parserAllocScope, input.get(), srcBuf);
697 if (!compiler.init(&fc, scopeCache, InheritThis::Yes, enclosingEnv)) {
698 return nullptr;
701 uint32_t len = srcBuf.length();
702 SourceExtent extent = SourceExtent::makeGlobalExtent(
703 len, options.lineno,
704 JS::LimitedColumnNumberOneOrigin::fromUnlimited(
705 JS::ColumnNumberOneOrigin(options.column)));
706 EvalSharedContext evalsc(&fc, compiler.compilationState(), extent);
707 if (!compiler.compile(cx, &evalsc)) {
708 return nullptr;
711 Rooted<CompilationGCOutput> gcOutput(cx);
713 BorrowingCompilationStencil borrowingStencil(compiler.stencil());
714 if (!InstantiateStencils(cx, input.get(), borrowingStencil,
715 gcOutput.get())) {
716 return nullptr;
720 assertException.reset();
721 script = gcOutput.get().script;
723 return script;
726 JSScript* frontend::CompileEvalScript(JSContext* cx,
727 const JS::ReadOnlyCompileOptions& options,
728 JS::SourceText<char16_t>& srcBuf,
729 JS::Handle<js::Scope*> enclosingScope,
730 JS::Handle<JSObject*> enclosingEnv) {
731 return CompileEvalScriptImpl(cx, options, srcBuf, enclosingScope,
732 enclosingEnv);
735 template <typename Unit>
736 class MOZ_STACK_CLASS ModuleCompiler final : public SourceAwareCompiler<Unit> {
737 using Base = SourceAwareCompiler<Unit>;
739 using Base::assertSourceParserAndScriptCreated;
740 using Base::compilationState_;
741 using Base::emplaceEmitter;
742 using Base::parser;
744 public:
745 explicit ModuleCompiler(FrontendContext* fc, LifoAllocScope& parserAllocScope,
746 CompilationInput& input,
747 SourceText<Unit>& sourceBuffer)
748 : Base(fc, parserAllocScope, input, sourceBuffer) {}
750 using Base::init;
751 using Base::stencil;
753 [[nodiscard]] bool compile(JSContext* maybeCx, FrontendContext* fc);
756 template <typename Unit>
757 class MOZ_STACK_CLASS StandaloneFunctionCompiler final
758 : public SourceAwareCompiler<Unit> {
759 using Base = SourceAwareCompiler<Unit>;
761 using Base::assertSourceAndParserCreated;
762 using Base::canHandleParseFailure;
763 using Base::compilationState_;
764 using Base::emplaceEmitter;
765 using Base::handleParseFailure;
766 using Base::parser;
767 using Base::sourceBuffer_;
769 using typename Base::TokenStreamPosition;
771 public:
772 explicit StandaloneFunctionCompiler(FrontendContext* fc,
773 LifoAllocScope& parserAllocScope,
774 CompilationInput& input,
775 SourceText<Unit>& sourceBuffer)
776 : Base(fc, parserAllocScope, input, sourceBuffer) {}
778 using Base::init;
779 using Base::stencil;
781 private:
782 FunctionNode* parse(JSContext* cx, FunctionSyntaxKind syntaxKind,
783 GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
784 const Maybe<uint32_t>& parameterListEnd);
786 public:
787 [[nodiscard]] bool compile(JSContext* cx, FunctionSyntaxKind syntaxKind,
788 GeneratorKind generatorKind,
789 FunctionAsyncKind asyncKind,
790 const Maybe<uint32_t>& parameterListEnd);
793 template <typename Unit>
794 bool SourceAwareCompiler<Unit>::createSourceAndParser(FrontendContext* fc) {
795 const auto& options = compilationState_.input.options;
797 fc_ = fc;
799 if (!compilationState_.source->assignSource(fc, options, sourceBuffer_)) {
800 return false;
803 MOZ_ASSERT(compilationState_.canLazilyParse ==
804 CanLazilyParse(compilationState_.input.options));
805 if (compilationState_.canLazilyParse) {
806 syntaxParser.emplace(fc_, options, sourceBuffer_.units(),
807 sourceBuffer_.length(),
808 /* foldConstants = */ false, compilationState_,
809 /* syntaxParser = */ nullptr);
810 if (!syntaxParser->checkOptions()) {
811 return false;
815 parser.emplace(fc_, options, sourceBuffer_.units(), sourceBuffer_.length(),
816 /* foldConstants = */ true, compilationState_,
817 syntaxParser.ptrOr(nullptr));
818 parser->ss = compilationState_.source.get();
819 return parser->checkOptions();
822 static bool EmplaceEmitter(CompilationState& compilationState,
823 Maybe<BytecodeEmitter>& emitter, FrontendContext* fc,
824 const EitherParser& parser, SharedContext* sc) {
825 BytecodeEmitter::EmitterMode emitterMode =
826 sc->selfHosted() ? BytecodeEmitter::SelfHosting : BytecodeEmitter::Normal;
827 emitter.emplace(fc, parser, sc, compilationState, emitterMode);
828 return emitter->init();
831 template <typename Unit>
832 bool SourceAwareCompiler<Unit>::canHandleParseFailure(
833 const Directives& newDirectives) {
834 // Try to reparse if no parse errors were thrown and the directives changed.
836 // NOTE:
837 // Only the following two directive changes force us to reparse the script:
838 // - The "use asm" directive was encountered.
839 // - The "use strict" directive was encountered and duplicate parameter names
840 // are present. We reparse in this case to display the error at the correct
841 // source location. See |Parser::hasValidSimpleStrictParameterNames()|.
842 return !parser->anyChars.hadError() &&
843 compilationState_.directives != newDirectives;
846 template <typename Unit>
847 void SourceAwareCompiler<Unit>::handleParseFailure(
848 const Directives& newDirectives, TokenStreamPosition& startPosition,
849 CompilationState::CompilationStatePosition& startStatePosition) {
850 MOZ_ASSERT(canHandleParseFailure(newDirectives));
852 // Rewind to starting position to retry.
853 parser->tokenStream.rewind(startPosition);
854 compilationState_.rewind(startStatePosition);
856 // Assignment must be monotonic to prevent reparsing iloops
857 MOZ_ASSERT_IF(compilationState_.directives.strict(), newDirectives.strict());
858 MOZ_ASSERT_IF(compilationState_.directives.asmJS(), newDirectives.asmJS());
859 compilationState_.directives = newDirectives;
862 static bool UsesExtraBindings(GlobalSharedContext* globalsc,
863 const ExtraBindingInfoVector& extraBindings,
864 const UsedNameTracker::UsedNameMap& usedNameMap) {
865 for (const auto& bindingInfo : extraBindings) {
866 if (bindingInfo.isShadowed) {
867 continue;
870 for (auto r = usedNameMap.all(); !r.empty(); r.popFront()) {
871 const auto& item = r.front();
872 const auto& name = item.key();
873 if (bindingInfo.nameIndex != name) {
874 continue;
877 const auto& nameInfo = item.value();
878 if (nameInfo.empty()) {
879 continue;
882 // This name is free, and uses the extra binding.
883 return true;
887 return false;
890 template <typename Unit>
891 bool ScriptCompiler<Unit>::popupateExtraBindingsFields(
892 GlobalSharedContext* globalsc) {
893 if (!compilationState_.input.internExtraBindings(
894 this->fc_, compilationState_.parserAtoms)) {
895 return false;
898 bool hasNonShadowedBinding = false;
899 for (auto& bindingInfo : compilationState_.input.extraBindings()) {
900 if (bindingInfo.isShadowed) {
901 continue;
904 bool isShadowed = false;
906 if (globalsc->bindings) {
907 for (ParserBindingIter bi(*globalsc->bindings); bi; bi++) {
908 if (bindingInfo.nameIndex == bi.name()) {
909 isShadowed = true;
910 break;
915 bindingInfo.isShadowed = isShadowed;
916 if (!isShadowed) {
917 hasNonShadowedBinding = true;
921 if (!hasNonShadowedBinding) {
922 // All bindings are shadowed.
923 this->fc_->reportExtraBindingsAreNotUsed();
924 return false;
927 if (globalsc->hasDirectEval()) {
928 // Direct eval can contain reference.
929 return true;
932 if (!UsesExtraBindings(globalsc, compilationState_.input.extraBindings(),
933 parser->usedNames().map())) {
934 this->fc_->reportExtraBindingsAreNotUsed();
935 return false;
938 return true;
941 template <typename Unit>
942 bool ScriptCompiler<Unit>::compile(JSContext* maybeCx, SharedContext* sc) {
943 assertSourceParserAndScriptCreated();
945 TokenStreamPosition startPosition(parser->tokenStream);
947 // Emplace the topLevel stencil
948 MOZ_ASSERT(compilationState_.scriptData.length() ==
949 CompilationStencil::TopLevelIndex);
950 if (!compilationState_.appendScriptStencilAndData(sc->fc_)) {
951 return false;
954 ParseNode* pn;
956 Maybe<AutoGeckoProfilerEntry> pseudoFrame;
957 if (maybeCx) {
958 pseudoFrame.emplace(maybeCx, "script parsing",
959 JS::ProfilingCategoryPair::JS_Parsing);
961 if (sc->isEvalContext()) {
962 pn = parser->evalBody(sc->asEvalContext()).unwrapOr(nullptr);
963 } else {
964 pn = parser->globalBody(sc->asGlobalContext()).unwrapOr(nullptr);
968 if (!pn) {
969 // Global and eval scripts don't get reparsed after a new directive was
970 // encountered:
971 // - "use strict" doesn't require any special error reporting for scripts.
972 // - "use asm" directives don't have an effect in global/eval contexts.
973 MOZ_ASSERT(!canHandleParseFailure(compilationState_.directives));
974 return false;
977 if (sc->isGlobalContext() && compilationState_.input.hasExtraBindings()) {
978 if (!popupateExtraBindingsFields(sc->asGlobalContext())) {
979 return false;
984 // Successfully parsed. Emit the script.
985 Maybe<AutoGeckoProfilerEntry> pseudoFrame;
986 if (maybeCx) {
987 pseudoFrame.emplace(maybeCx, "script emit",
988 JS::ProfilingCategoryPair::JS_Parsing);
991 Maybe<BytecodeEmitter> emitter;
992 if (!emplaceEmitter(emitter, sc)) {
993 return false;
996 if (!emitter->emitScript(pn)) {
997 return false;
1001 MOZ_ASSERT(!this->fc_->hadErrors());
1003 return true;
1006 template <typename Unit>
1007 bool ModuleCompiler<Unit>::compile(JSContext* maybeCx, FrontendContext* fc) {
1008 // Emplace the topLevel stencil
1009 MOZ_ASSERT(compilationState_.scriptData.length() ==
1010 CompilationStencil::TopLevelIndex);
1011 if (!compilationState_.appendScriptStencilAndData(fc)) {
1012 return false;
1015 ModuleBuilder builder(fc, parser.ptr());
1017 const auto& options = compilationState_.input.options;
1019 uint32_t len = this->sourceBuffer_.length();
1020 SourceExtent extent = SourceExtent::makeGlobalExtent(
1021 len, options.lineno,
1022 JS::LimitedColumnNumberOneOrigin::fromUnlimited(
1023 JS::ColumnNumberOneOrigin(options.column)));
1024 ModuleSharedContext modulesc(fc, options, builder, extent);
1026 ParseNode* pn = parser->moduleBody(&modulesc).unwrapOr(nullptr);
1027 if (!pn) {
1028 return false;
1031 Maybe<BytecodeEmitter> emitter;
1032 if (!emplaceEmitter(emitter, &modulesc)) {
1033 return false;
1036 if (!emitter->emitScript(pn->as<ModuleNode>().body())) {
1037 return false;
1040 StencilModuleMetadata& moduleMetadata = *compilationState_.moduleMetadata;
1042 builder.finishFunctionDecls(moduleMetadata);
1044 MOZ_ASSERT(!this->fc_->hadErrors());
1046 return true;
1049 // Parse a standalone JS function, which might appear as the value of an
1050 // event handler attribute in an HTML <INPUT> tag, or in a Function()
1051 // constructor.
1052 template <typename Unit>
1053 FunctionNode* StandaloneFunctionCompiler<Unit>::parse(
1054 JSContext* cx, FunctionSyntaxKind syntaxKind, GeneratorKind generatorKind,
1055 FunctionAsyncKind asyncKind, const Maybe<uint32_t>& parameterListEnd) {
1056 assertSourceAndParserCreated();
1058 TokenStreamPosition startPosition(parser->tokenStream);
1059 auto startStatePosition = compilationState_.getPosition();
1061 // Speculatively parse using the default directives implied by the context.
1062 // If a directive is encountered (e.g., "use strict") that changes how the
1063 // function should have been parsed, we backup and reparse with the new set
1064 // of directives.
1066 FunctionNode* fn;
1067 for (;;) {
1068 Directives newDirectives = compilationState_.directives;
1069 fn = parser
1070 ->standaloneFunction(parameterListEnd, syntaxKind, generatorKind,
1071 asyncKind, compilationState_.directives,
1072 &newDirectives)
1073 .unwrapOr(nullptr);
1074 if (fn) {
1075 break;
1078 // Maybe we encountered a new directive. See if we can try again.
1079 if (!canHandleParseFailure(newDirectives)) {
1080 return nullptr;
1083 handleParseFailure(newDirectives, startPosition, startStatePosition);
1086 return fn;
1089 // Compile a standalone JS function.
1090 template <typename Unit>
1091 bool StandaloneFunctionCompiler<Unit>::compile(
1092 JSContext* cx, FunctionSyntaxKind syntaxKind, GeneratorKind generatorKind,
1093 FunctionAsyncKind asyncKind, const Maybe<uint32_t>& parameterListEnd) {
1094 FunctionNode* parsedFunction =
1095 parse(cx, syntaxKind, generatorKind, asyncKind, parameterListEnd);
1096 if (!parsedFunction) {
1097 return false;
1100 FunctionBox* funbox = parsedFunction->funbox();
1102 if (funbox->isInterpreted()) {
1103 Maybe<BytecodeEmitter> emitter;
1104 if (!emplaceEmitter(emitter, funbox)) {
1105 return false;
1108 if (!emitter->emitFunctionScript(parsedFunction)) {
1109 return false;
1112 // The parser extent has stripped off the leading `function...` but
1113 // we want the SourceExtent used in the final standalone script to
1114 // start from the beginning of the buffer, and use the provided
1115 // line and column.
1116 const auto& options = compilationState_.input.options;
1117 compilationState_.scriptExtra[CompilationStencil::TopLevelIndex].extent =
1118 SourceExtent{/* sourceStart = */ 0,
1119 sourceBuffer_.length(),
1120 funbox->extent().toStringStart,
1121 funbox->extent().toStringEnd,
1122 options.lineno,
1123 JS::LimitedColumnNumberOneOrigin::fromUnlimited(
1124 JS::ColumnNumberOneOrigin(options.column))};
1125 } else {
1126 // The asm.js module was created by parser. Instantiation below will
1127 // allocate the JSFunction that wraps it.
1128 MOZ_ASSERT(funbox->isAsmJSModule());
1129 MOZ_ASSERT(compilationState_.asmJS->moduleMap.has(funbox->index()));
1130 MOZ_ASSERT(compilationState_.scriptData[CompilationStencil::TopLevelIndex]
1131 .functionFlags.isAsmJSNative());
1134 return true;
1137 // Compile module, and return it as one of:
1138 // * ExtensibleCompilationStencil (without instantiation)
1139 // * CompilationStencil (without instantiation, has no external dependency)
1140 // * CompilationGCOutput (with instantiation).
1141 template <typename Unit>
1142 [[nodiscard]] static bool ParseModuleToStencilAndMaybeInstantiate(
1143 JSContext* maybeCx, FrontendContext* fc, js::LifoAlloc& tempLifoAlloc,
1144 CompilationInput& input, ScopeBindingCache* scopeCache,
1145 SourceText<Unit>& srcBuf, BytecodeCompilerOutput& output) {
1146 MOZ_ASSERT(srcBuf.get());
1147 MOZ_ASSERT(input.options.lineno != 0,
1148 "Module cannot be compiled with lineNumber == 0");
1150 if (!input.initForModule(fc)) {
1151 return false;
1154 AutoAssertReportedException assertException(maybeCx, fc);
1156 LifoAllocScope parserAllocScope(&tempLifoAlloc);
1157 ModuleCompiler<Unit> compiler(fc, parserAllocScope, input, srcBuf);
1158 if (!compiler.init(fc, scopeCache)) {
1159 return false;
1162 if (!compiler.compile(maybeCx, fc)) {
1163 return false;
1166 if (output.is<UniquePtr<ExtensibleCompilationStencil>>()) {
1167 auto stencil =
1168 fc->getAllocator()->make_unique<ExtensibleCompilationStencil>(
1169 std::move(compiler.stencil()));
1170 if (!stencil) {
1171 return false;
1173 output.as<UniquePtr<ExtensibleCompilationStencil>>() = std::move(stencil);
1174 } else if (output.is<RefPtr<CompilationStencil>>()) {
1175 Maybe<AutoGeckoProfilerEntry> pseudoFrame;
1176 if (maybeCx) {
1177 pseudoFrame.emplace(maybeCx, "script emit",
1178 JS::ProfilingCategoryPair::JS_Parsing);
1181 auto extensibleStencil =
1182 fc->getAllocator()->make_unique<frontend::ExtensibleCompilationStencil>(
1183 std::move(compiler.stencil()));
1184 if (!extensibleStencil) {
1185 return false;
1188 RefPtr<CompilationStencil> stencil =
1189 fc->getAllocator()->new_<CompilationStencil>(
1190 std::move(extensibleStencil));
1191 if (!stencil) {
1192 return false;
1195 output.as<RefPtr<CompilationStencil>>() = std::move(stencil);
1196 } else {
1197 MOZ_ASSERT(maybeCx);
1198 BorrowingCompilationStencil borrowingStencil(compiler.stencil());
1199 if (!InstantiateStencils(maybeCx, input, borrowingStencil,
1200 *(output.as<CompilationGCOutput*>()))) {
1201 return false;
1205 assertException.reset();
1206 return true;
1209 template <typename Unit>
1210 already_AddRefed<CompilationStencil> ParseModuleToStencilImpl(
1211 JSContext* maybeCx, FrontendContext* fc, js::LifoAlloc& tempLifoAlloc,
1212 CompilationInput& input, ScopeBindingCache* scopeCache,
1213 SourceText<Unit>& srcBuf) {
1214 using OutputType = RefPtr<CompilationStencil>;
1215 BytecodeCompilerOutput output((OutputType()));
1216 if (!ParseModuleToStencilAndMaybeInstantiate(
1217 maybeCx, fc, tempLifoAlloc, input, scopeCache, srcBuf, output)) {
1218 return nullptr;
1220 return output.as<OutputType>().forget();
1223 already_AddRefed<CompilationStencil> frontend::ParseModuleToStencil(
1224 JSContext* maybeCx, FrontendContext* fc, js::LifoAlloc& tempLifoAlloc,
1225 CompilationInput& input, ScopeBindingCache* scopeCache,
1226 SourceText<char16_t>& srcBuf) {
1227 return ParseModuleToStencilImpl(maybeCx, fc, tempLifoAlloc, input, scopeCache,
1228 srcBuf);
1231 already_AddRefed<CompilationStencil> frontend::ParseModuleToStencil(
1232 JSContext* maybeCx, FrontendContext* fc, js::LifoAlloc& tempLifoAlloc,
1233 CompilationInput& input, ScopeBindingCache* scopeCache,
1234 SourceText<Utf8Unit>& srcBuf) {
1235 return ParseModuleToStencilImpl(maybeCx, fc, tempLifoAlloc, input, scopeCache,
1236 srcBuf);
1239 template <typename Unit>
1240 UniquePtr<ExtensibleCompilationStencil> ParseModuleToExtensibleStencilImpl(
1241 JSContext* cx, FrontendContext* fc, js::LifoAlloc& tempLifoAlloc,
1242 CompilationInput& input, ScopeBindingCache* scopeCache,
1243 SourceText<Unit>& srcBuf) {
1244 using OutputType = UniquePtr<ExtensibleCompilationStencil>;
1245 BytecodeCompilerOutput output((OutputType()));
1246 if (!ParseModuleToStencilAndMaybeInstantiate(cx, fc, tempLifoAlloc, input,
1247 scopeCache, srcBuf, output)) {
1248 return nullptr;
1250 return std::move(output.as<OutputType>());
1253 UniquePtr<ExtensibleCompilationStencil>
1254 frontend::ParseModuleToExtensibleStencil(JSContext* cx, FrontendContext* fc,
1255 js::LifoAlloc& tempLifoAlloc,
1256 CompilationInput& input,
1257 ScopeBindingCache* scopeCache,
1258 SourceText<char16_t>& srcBuf) {
1259 return ParseModuleToExtensibleStencilImpl(cx, fc, tempLifoAlloc, input,
1260 scopeCache, srcBuf);
1263 UniquePtr<ExtensibleCompilationStencil>
1264 frontend::ParseModuleToExtensibleStencil(JSContext* cx, FrontendContext* fc,
1265 js::LifoAlloc& tempLifoAlloc,
1266 CompilationInput& input,
1267 ScopeBindingCache* scopeCache,
1268 SourceText<Utf8Unit>& srcBuf) {
1269 return ParseModuleToExtensibleStencilImpl(cx, fc, tempLifoAlloc, input,
1270 scopeCache, srcBuf);
1273 template <typename Unit>
1274 static ModuleObject* CompileModuleImpl(
1275 JSContext* cx, FrontendContext* fc,
1276 const JS::ReadOnlyCompileOptions& optionsInput, SourceText<Unit>& srcBuf) {
1277 AutoAssertReportedException assertException(cx, fc);
1279 CompileOptions options(cx, optionsInput);
1280 options.setModule();
1282 Rooted<CompilationInput> input(cx, CompilationInput(options));
1283 Rooted<CompilationGCOutput> gcOutput(cx);
1284 BytecodeCompilerOutput output(gcOutput.address());
1286 NoScopeBindingCache scopeCache;
1287 if (!ParseModuleToStencilAndMaybeInstantiate(cx, fc, cx->tempLifoAlloc(),
1288 input.get(), &scopeCache, srcBuf,
1289 output)) {
1290 return nullptr;
1293 assertException.reset();
1294 return gcOutput.get().module;
1297 ModuleObject* frontend::CompileModule(JSContext* cx, FrontendContext* fc,
1298 const JS::ReadOnlyCompileOptions& options,
1299 SourceText<char16_t>& srcBuf) {
1300 return CompileModuleImpl(cx, fc, options, srcBuf);
1303 ModuleObject* frontend::CompileModule(JSContext* cx, FrontendContext* fc,
1304 const JS::ReadOnlyCompileOptions& options,
1305 SourceText<Utf8Unit>& srcBuf) {
1306 return CompileModuleImpl(cx, fc, options, srcBuf);
1309 static bool InstantiateLazyFunction(JSContext* cx, CompilationInput& input,
1310 CompilationStencil& stencil,
1311 BytecodeCompilerOutput& output) {
1312 // We do check the type, but do not write anything to it as this is not
1313 // necessary for lazy function, as the script is patched inside the
1314 // JSFunction when instantiating.
1315 MOZ_ASSERT(output.is<CompilationGCOutput*>());
1316 MOZ_ASSERT(!output.as<CompilationGCOutput*>());
1318 mozilla::DebugOnly<uint32_t> lazyFlags =
1319 static_cast<uint32_t>(input.immutableFlags());
1321 Rooted<CompilationGCOutput> gcOutput(cx);
1323 if (input.source->hasEncoder()) {
1324 if (!input.source->addDelazificationToIncrementalEncoding(cx, stencil)) {
1325 return false;
1329 if (!CompilationStencil::instantiateStencils(cx, input, stencil,
1330 gcOutput.get())) {
1331 return false;
1334 // NOTE: After instantiation succeeds and bytecode is attached, the rest of
1335 // this operation should be infallible. Any failure during
1336 // delazification should restore the function back to a consistent
1337 // lazy state.
1339 MOZ_ASSERT(lazyFlags == gcOutput.get().script->immutableFlags());
1340 MOZ_ASSERT(gcOutput.get().script->outermostScope()->hasOnChain(
1341 ScopeKind::NonSyntactic) ==
1342 gcOutput.get().script->immutableFlags().hasFlag(
1343 JSScript::ImmutableFlags::HasNonSyntacticScope));
1345 return true;
1348 enum class GetCachedResult {
1349 // Similar to return false.
1350 Error,
1352 // We have not found any entry.
1353 NotFound,
1355 // We have found an entry, and set everything according to the desired
1356 // BytecodeCompilerOutput out-param.
1357 Found
1360 // When we have a cache hit, the addPtr out-param would evaluate to a true-ish
1361 // value.
1362 static GetCachedResult GetCachedLazyFunctionStencilMaybeInstantiate(
1363 JSContext* maybeCx, FrontendContext* fc, CompilationInput& input,
1364 BytecodeCompilerOutput& output) {
1365 RefPtr<CompilationStencil> stencil;
1367 DelazificationCache& cache = DelazificationCache::getSingleton();
1368 auto guard = cache.isSourceCached(input.source);
1369 if (!guard) {
1370 return GetCachedResult::NotFound;
1373 // Before releasing the guard, which is locking the cache, we increment the
1374 // reference counter such that we do not reclaim the CompilationStencil
1375 // while we are instantiating it.
1376 StencilContext key(input.source, input.extent());
1377 stencil = cache.lookup(guard, key);
1378 if (!stencil) {
1379 return GetCachedResult::NotFound;
1383 if (output.is<RefPtr<CompilationStencil>>()) {
1384 output.as<RefPtr<CompilationStencil>>() = stencil;
1385 return GetCachedResult::Found;
1388 if (output.is<UniquePtr<ExtensibleCompilationStencil>>()) {
1389 auto extensible =
1390 fc->getAllocator()->make_unique<ExtensibleCompilationStencil>(input);
1391 if (!extensible) {
1392 return GetCachedResult::Error;
1394 if (!extensible->cloneFrom(fc, *stencil)) {
1395 return GetCachedResult::Error;
1398 output.as<UniquePtr<ExtensibleCompilationStencil>>() =
1399 std::move(extensible);
1400 return GetCachedResult::Found;
1403 MOZ_ASSERT(maybeCx);
1405 if (!InstantiateLazyFunction(maybeCx, input, *stencil, output)) {
1406 return GetCachedResult::Error;
1409 return GetCachedResult::Found;
1412 template <typename Unit>
1413 static bool CompileLazyFunctionToStencilMaybeInstantiate(
1414 JSContext* maybeCx, FrontendContext* fc, js::LifoAlloc& tempLifoAlloc,
1415 CompilationInput& input, ScopeBindingCache* scopeCache, const Unit* units,
1416 size_t length, BytecodeCompilerOutput& output) {
1417 MOZ_ASSERT(input.source);
1419 AutoAssertReportedException assertException(maybeCx, fc);
1420 if (input.options.consumeDelazificationCache()) {
1421 auto res = GetCachedLazyFunctionStencilMaybeInstantiate(maybeCx, fc, input,
1422 output);
1423 switch (res) {
1424 case GetCachedResult::Error:
1425 return false;
1426 case GetCachedResult::Found:
1427 assertException.reset();
1428 return true;
1429 case GetCachedResult::NotFound:
1430 break;
1434 InheritThis inheritThis =
1435 input.functionFlags().isArrow() ? InheritThis::Yes : InheritThis::No;
1437 LifoAllocScope parserAllocScope(&tempLifoAlloc);
1438 CompilationState compilationState(fc, parserAllocScope, input);
1439 compilationState.setFunctionKey(input.extent());
1440 MOZ_ASSERT(!compilationState.isInitialStencil());
1441 if (!compilationState.init(fc, scopeCache, inheritThis)) {
1442 return false;
1445 Parser<FullParseHandler, Unit> parser(fc, input.options, units, length,
1446 /* foldConstants = */ true,
1447 compilationState,
1448 /* syntaxParser = */ nullptr);
1449 if (!parser.checkOptions()) {
1450 return false;
1453 FunctionNode* pn =
1454 parser
1455 .standaloneLazyFunction(input, input.extent().toStringStart,
1456 input.strict(), input.generatorKind(),
1457 input.asyncKind())
1458 .unwrapOr(nullptr);
1459 if (!pn) {
1460 return false;
1463 BytecodeEmitter bce(fc, &parser, pn->funbox(), compilationState,
1464 BytecodeEmitter::LazyFunction);
1465 if (!bce.init(pn->pn_pos)) {
1466 return false;
1469 if (!bce.emitFunctionScript(pn)) {
1470 return false;
1473 // NOTE: Only allow relazification if there was no lazy PrivateScriptData.
1474 // This excludes non-leaf functions and all script class constructors.
1475 bool hadLazyScriptData = input.hasPrivateScriptData();
1476 bool isRelazifiableAfterDelazify = input.isRelazifiable();
1477 if (isRelazifiableAfterDelazify && !hadLazyScriptData) {
1478 compilationState.scriptData[CompilationStencil::TopLevelIndex]
1479 .setAllowRelazify();
1482 if (input.options.checkDelazificationCache()) {
1483 using OutputType = RefPtr<CompilationStencil>;
1484 BytecodeCompilerOutput cached((OutputType()));
1485 auto res = GetCachedLazyFunctionStencilMaybeInstantiate(nullptr, fc, input,
1486 cached);
1487 if (res == GetCachedResult::Error) {
1488 return false;
1490 // Cached results might be removed by GCs.
1491 if (res == GetCachedResult::Found) {
1492 auto& concurrentSharedData = cached.as<OutputType>().get()->sharedData;
1493 auto concurrentData =
1494 concurrentSharedData.isSingle()
1495 ? concurrentSharedData.asSingle()->get()->immutableData()
1496 : concurrentSharedData.asBorrow()
1497 ->asSingle()
1498 ->get()
1499 ->immutableData();
1500 auto ondemandData =
1501 compilationState.sharedData.asSingle()->get()->immutableData();
1502 MOZ_RELEASE_ASSERT(concurrentData.Length() == ondemandData.Length(),
1503 "Non-deterministic stencils");
1504 for (size_t i = 0; i < concurrentData.Length(); i++) {
1505 MOZ_RELEASE_ASSERT(concurrentData[i] == ondemandData[i],
1506 "Non-deterministic stencils");
1511 if (output.is<UniquePtr<ExtensibleCompilationStencil>>()) {
1512 auto stencil =
1513 fc->getAllocator()->make_unique<ExtensibleCompilationStencil>(
1514 std::move(compilationState));
1515 if (!stencil) {
1516 return false;
1518 output.as<UniquePtr<ExtensibleCompilationStencil>>() = std::move(stencil);
1519 } else if (output.is<RefPtr<CompilationStencil>>()) {
1520 Maybe<AutoGeckoProfilerEntry> pseudoFrame;
1521 if (maybeCx) {
1522 pseudoFrame.emplace(maybeCx, "script emit",
1523 JS::ProfilingCategoryPair::JS_Parsing);
1526 auto extensibleStencil =
1527 fc->getAllocator()->make_unique<frontend::ExtensibleCompilationStencil>(
1528 std::move(compilationState));
1529 if (!extensibleStencil) {
1530 return false;
1533 RefPtr<CompilationStencil> stencil =
1534 fc->getAllocator()->new_<CompilationStencil>(
1535 std::move(extensibleStencil));
1536 if (!stencil) {
1537 return false;
1540 output.as<RefPtr<CompilationStencil>>() = std::move(stencil);
1541 } else {
1542 MOZ_ASSERT(maybeCx);
1543 BorrowingCompilationStencil borrowingStencil(compilationState);
1544 if (!InstantiateLazyFunction(maybeCx, input, borrowingStencil, output)) {
1545 return false;
1549 assertException.reset();
1550 return true;
1553 template <typename Unit>
1554 static bool DelazifyCanonicalScriptedFunctionImpl(JSContext* cx,
1555 FrontendContext* fc,
1556 ScopeBindingCache* scopeCache,
1557 JS::Handle<JSFunction*> fun,
1558 JS::Handle<BaseScript*> lazy,
1559 ScriptSource* ss) {
1560 MOZ_ASSERT(!lazy->hasBytecode(), "Script is already compiled!");
1561 MOZ_ASSERT(lazy->function() == fun);
1563 MOZ_DIAGNOSTIC_ASSERT(!fun->isGhost());
1565 AutoIncrementalTimer timer(cx->realm()->timers.delazificationTime);
1567 size_t sourceStart = lazy->sourceStart();
1568 size_t sourceLength = lazy->sourceEnd() - lazy->sourceStart();
1570 MOZ_ASSERT(ss->hasSourceText());
1572 // Parse and compile the script from source.
1573 UncompressedSourceCache::AutoHoldEntry holder;
1575 MOZ_ASSERT(ss->hasSourceType<Unit>());
1577 ScriptSource::PinnedUnits<Unit> units(cx, ss, holder, sourceStart,
1578 sourceLength);
1579 if (!units.get()) {
1580 return false;
1583 JS::CompileOptions options(cx);
1584 options.setMutedErrors(lazy->mutedErrors())
1585 .setFileAndLine(lazy->filename(), lazy->lineno())
1586 .setColumn(JS::ColumnNumberOneOrigin(lazy->column()))
1587 .setScriptSourceOffset(lazy->sourceStart())
1588 .setNoScriptRval(false)
1589 .setSelfHostingMode(false)
1590 .setEagerDelazificationStrategy(lazy->delazificationMode());
1592 Rooted<CompilationInput> input(cx, CompilationInput(options));
1593 input.get().initFromLazy(cx, lazy, ss);
1595 CompilationGCOutput* unusedGcOutput = nullptr;
1596 BytecodeCompilerOutput output(unusedGcOutput);
1597 return CompileLazyFunctionToStencilMaybeInstantiate(
1598 cx, fc, cx->tempLifoAlloc(), input.get(), scopeCache, units.get(),
1599 sourceLength, output);
1602 bool frontend::DelazifyCanonicalScriptedFunction(JSContext* cx,
1603 FrontendContext* fc,
1604 JS::Handle<JSFunction*> fun) {
1605 Maybe<AutoGeckoProfilerEntry> pseudoFrame;
1606 if (cx) {
1607 pseudoFrame.emplace(cx, "script delazify",
1608 JS::ProfilingCategoryPair::JS_Parsing);
1611 Rooted<BaseScript*> lazy(cx, fun->baseScript());
1612 ScriptSource* ss = lazy->scriptSource();
1613 ScopeBindingCache* scopeCache = &cx->caches().scopeCache;
1615 if (ss->hasSourceType<Utf8Unit>()) {
1616 // UTF-8 source text.
1617 return DelazifyCanonicalScriptedFunctionImpl<Utf8Unit>(cx, fc, scopeCache,
1618 fun, lazy, ss);
1621 MOZ_ASSERT(ss->hasSourceType<char16_t>());
1623 // UTF-16 source text.
1624 return DelazifyCanonicalScriptedFunctionImpl<char16_t>(cx, fc, scopeCache,
1625 fun, lazy, ss);
1628 template <typename Unit>
1629 static already_AddRefed<CompilationStencil>
1630 DelazifyCanonicalScriptedFunctionImpl(
1631 FrontendContext* fc, js::LifoAlloc& tempLifoAlloc,
1632 const JS::PrefableCompileOptions& prefableOptions,
1633 ScopeBindingCache* scopeCache, CompilationStencil& context,
1634 ScriptIndex scriptIndex, DelazifyFailureReason* failureReason) {
1635 ScriptStencilRef script{context, scriptIndex};
1636 const ScriptStencilExtra& extra = script.scriptExtra();
1638 #if defined(EARLY_BETA_OR_EARLIER) || defined(DEBUG)
1639 const ScriptStencil& data = script.scriptData();
1640 MOZ_ASSERT(!data.hasSharedData(), "Script is already compiled!");
1641 MOZ_DIAGNOSTIC_ASSERT(!data.isGhost());
1642 #endif
1644 size_t sourceStart = extra.extent.sourceStart;
1645 size_t sourceLength = extra.extent.sourceEnd - sourceStart;
1647 ScriptSource* ss = context.source;
1648 MOZ_ASSERT(ss->hasSourceText());
1650 MOZ_ASSERT(ss->hasSourceType<Unit>());
1652 ScriptSource::PinnedUnitsIfUncompressed<Unit> units(ss, sourceStart,
1653 sourceLength);
1654 if (!units.get()) {
1655 *failureReason = DelazifyFailureReason::Compressed;
1656 return nullptr;
1659 JS::CompileOptions options(prefableOptions);
1660 options.setMutedErrors(ss->mutedErrors())
1661 .setFileAndLine(ss->filename(), extra.extent.lineno)
1662 .setColumn(JS::ColumnNumberOneOrigin(extra.extent.column))
1663 .setScriptSourceOffset(sourceStart)
1664 .setNoScriptRval(false)
1665 .setSelfHostingMode(false);
1667 // CompilationInput initialized with initFromStencil only reference
1668 // information from the CompilationStencil context and the ref-counted
1669 // ScriptSource, which are both GC-free.
1670 JS_HAZ_NON_GC_POINTER CompilationInput input(options);
1671 input.initFromStencil(context, scriptIndex, ss);
1673 using OutputType = RefPtr<CompilationStencil>;
1674 BytecodeCompilerOutput output((OutputType()));
1675 if (!CompileLazyFunctionToStencilMaybeInstantiate(
1676 nullptr, fc, tempLifoAlloc, input, scopeCache, units.get(),
1677 sourceLength, output)) {
1678 *failureReason = DelazifyFailureReason::Other;
1679 return nullptr;
1681 return output.as<OutputType>().forget();
1684 already_AddRefed<CompilationStencil>
1685 frontend::DelazifyCanonicalScriptedFunction(
1686 FrontendContext* fc, js::LifoAlloc& tempLifoAlloc,
1687 const JS::PrefableCompileOptions& prefableOptions,
1688 ScopeBindingCache* scopeCache, CompilationStencil& context,
1689 ScriptIndex scriptIndex, DelazifyFailureReason* failureReason) {
1690 ScriptSource* ss = context.source;
1691 if (ss->hasSourceType<Utf8Unit>()) {
1692 // UTF-8 source text.
1693 return DelazifyCanonicalScriptedFunctionImpl<Utf8Unit>(
1694 fc, tempLifoAlloc, prefableOptions, scopeCache, context, scriptIndex,
1695 failureReason);
1698 // UTF-16 source text.
1699 MOZ_ASSERT(ss->hasSourceType<char16_t>());
1700 return DelazifyCanonicalScriptedFunctionImpl<char16_t>(
1701 fc, tempLifoAlloc, prefableOptions, scopeCache, context, scriptIndex,
1702 failureReason);
1705 static JSFunction* CompileStandaloneFunction(
1706 JSContext* cx, const JS::ReadOnlyCompileOptions& options,
1707 JS::SourceText<char16_t>& srcBuf, const Maybe<uint32_t>& parameterListEnd,
1708 FunctionSyntaxKind syntaxKind, GeneratorKind generatorKind,
1709 FunctionAsyncKind asyncKind, JS::Handle<Scope*> enclosingScope = nullptr) {
1710 JS::Rooted<JSFunction*> fun(cx);
1712 AutoReportFrontendContext fc(cx);
1713 AutoAssertReportedException assertException(cx, &fc);
1715 Rooted<CompilationInput> input(cx, CompilationInput(options));
1716 if (enclosingScope) {
1717 if (!input.get().initForStandaloneFunctionInNonSyntacticScope(
1718 &fc, enclosingScope)) {
1719 return nullptr;
1721 } else {
1722 if (!input.get().initForStandaloneFunction(cx, &fc)) {
1723 return nullptr;
1727 LifoAllocScope parserAllocScope(&cx->tempLifoAlloc());
1728 InheritThis inheritThis = (syntaxKind == FunctionSyntaxKind::Arrow)
1729 ? InheritThis::Yes
1730 : InheritThis::No;
1731 ScopeBindingCache* scopeCache = &cx->caches().scopeCache;
1732 StandaloneFunctionCompiler<char16_t> compiler(&fc, parserAllocScope,
1733 input.get(), srcBuf);
1734 if (!compiler.init(&fc, scopeCache, inheritThis)) {
1735 return nullptr;
1738 if (!compiler.compile(cx, syntaxKind, generatorKind, asyncKind,
1739 parameterListEnd)) {
1740 return nullptr;
1743 Rooted<CompilationGCOutput> gcOutput(cx);
1744 RefPtr<ScriptSource> source;
1746 BorrowingCompilationStencil borrowingStencil(compiler.stencil());
1747 if (!CompilationStencil::instantiateStencils(
1748 cx, input.get(), borrowingStencil, gcOutput.get())) {
1749 return nullptr;
1751 source = borrowingStencil.source;
1754 fun = gcOutput.get().getFunctionNoBaseIndex(
1755 CompilationStencil::TopLevelIndex);
1756 MOZ_ASSERT(fun->hasBytecode() || IsAsmJSModule(fun));
1758 // Enqueue an off-thread source compression task after finishing parsing.
1759 if (!source->tryCompressOffThread(cx)) {
1760 return nullptr;
1763 // Note: If AsmJS successfully compiles, the into.script will still be
1764 // nullptr. In this case we have compiled to a native function instead of an
1765 // interpreted script.
1766 if (gcOutput.get().script) {
1767 if (parameterListEnd) {
1768 source->setParameterListEnd(*parameterListEnd);
1771 const JS::InstantiateOptions instantiateOptions(options);
1772 Rooted<JSScript*> script(cx, gcOutput.get().script);
1773 FireOnNewScript(cx, instantiateOptions, script);
1776 assertException.reset();
1778 return fun;
1781 JSFunction* frontend::CompileStandaloneFunction(
1782 JSContext* cx, const JS::ReadOnlyCompileOptions& options,
1783 JS::SourceText<char16_t>& srcBuf, const Maybe<uint32_t>& parameterListEnd,
1784 FunctionSyntaxKind syntaxKind) {
1785 return CompileStandaloneFunction(cx, options, srcBuf, parameterListEnd,
1786 syntaxKind, GeneratorKind::NotGenerator,
1787 FunctionAsyncKind::SyncFunction);
1790 JSFunction* frontend::CompileStandaloneGenerator(
1791 JSContext* cx, const JS::ReadOnlyCompileOptions& options,
1792 JS::SourceText<char16_t>& srcBuf, const Maybe<uint32_t>& parameterListEnd,
1793 FunctionSyntaxKind syntaxKind) {
1794 return CompileStandaloneFunction(cx, options, srcBuf, parameterListEnd,
1795 syntaxKind, GeneratorKind::Generator,
1796 FunctionAsyncKind::SyncFunction);
1799 JSFunction* frontend::CompileStandaloneAsyncFunction(
1800 JSContext* cx, const ReadOnlyCompileOptions& options,
1801 JS::SourceText<char16_t>& srcBuf, const Maybe<uint32_t>& parameterListEnd,
1802 FunctionSyntaxKind syntaxKind) {
1803 return CompileStandaloneFunction(cx, options, srcBuf, parameterListEnd,
1804 syntaxKind, GeneratorKind::NotGenerator,
1805 FunctionAsyncKind::AsyncFunction);
1808 JSFunction* frontend::CompileStandaloneAsyncGenerator(
1809 JSContext* cx, const ReadOnlyCompileOptions& options,
1810 JS::SourceText<char16_t>& srcBuf, const Maybe<uint32_t>& parameterListEnd,
1811 FunctionSyntaxKind syntaxKind) {
1812 return CompileStandaloneFunction(cx, options, srcBuf, parameterListEnd,
1813 syntaxKind, GeneratorKind::Generator,
1814 FunctionAsyncKind::AsyncFunction);
1817 JSFunction* frontend::CompileStandaloneFunctionInNonSyntacticScope(
1818 JSContext* cx, const JS::ReadOnlyCompileOptions& options,
1819 JS::SourceText<char16_t>& srcBuf, const Maybe<uint32_t>& parameterListEnd,
1820 FunctionSyntaxKind syntaxKind, JS::Handle<Scope*> enclosingScope) {
1821 MOZ_ASSERT(enclosingScope);
1822 return CompileStandaloneFunction(cx, options, srcBuf, parameterListEnd,
1823 syntaxKind, GeneratorKind::NotGenerator,
1824 FunctionAsyncKind::SyncFunction,
1825 enclosingScope);