Bug 1865597 - Add error checking when initializing parallel marking and disable on...
[gecko.git] / js / src / frontend / BytecodeCompiler.cpp
blob7a86a3d003fc6f2374c33aa7336d84eec823ee8f
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());
1148 if (!input.initForModule(fc)) {
1149 return false;
1152 AutoAssertReportedException assertException(maybeCx, fc);
1154 LifoAllocScope parserAllocScope(&tempLifoAlloc);
1155 ModuleCompiler<Unit> compiler(fc, parserAllocScope, input, srcBuf);
1156 if (!compiler.init(fc, scopeCache)) {
1157 return false;
1160 if (!compiler.compile(maybeCx, fc)) {
1161 return false;
1164 if (output.is<UniquePtr<ExtensibleCompilationStencil>>()) {
1165 auto stencil =
1166 fc->getAllocator()->make_unique<ExtensibleCompilationStencil>(
1167 std::move(compiler.stencil()));
1168 if (!stencil) {
1169 return false;
1171 output.as<UniquePtr<ExtensibleCompilationStencil>>() = std::move(stencil);
1172 } else if (output.is<RefPtr<CompilationStencil>>()) {
1173 Maybe<AutoGeckoProfilerEntry> pseudoFrame;
1174 if (maybeCx) {
1175 pseudoFrame.emplace(maybeCx, "script emit",
1176 JS::ProfilingCategoryPair::JS_Parsing);
1179 auto extensibleStencil =
1180 fc->getAllocator()->make_unique<frontend::ExtensibleCompilationStencil>(
1181 std::move(compiler.stencil()));
1182 if (!extensibleStencil) {
1183 return false;
1186 RefPtr<CompilationStencil> stencil =
1187 fc->getAllocator()->new_<CompilationStencil>(
1188 std::move(extensibleStencil));
1189 if (!stencil) {
1190 return false;
1193 output.as<RefPtr<CompilationStencil>>() = std::move(stencil);
1194 } else {
1195 MOZ_ASSERT(maybeCx);
1196 BorrowingCompilationStencil borrowingStencil(compiler.stencil());
1197 if (!InstantiateStencils(maybeCx, input, borrowingStencil,
1198 *(output.as<CompilationGCOutput*>()))) {
1199 return false;
1203 assertException.reset();
1204 return true;
1207 template <typename Unit>
1208 already_AddRefed<CompilationStencil> ParseModuleToStencilImpl(
1209 JSContext* maybeCx, FrontendContext* fc, js::LifoAlloc& tempLifoAlloc,
1210 CompilationInput& input, ScopeBindingCache* scopeCache,
1211 SourceText<Unit>& srcBuf) {
1212 using OutputType = RefPtr<CompilationStencil>;
1213 BytecodeCompilerOutput output((OutputType()));
1214 if (!ParseModuleToStencilAndMaybeInstantiate(
1215 maybeCx, fc, tempLifoAlloc, input, scopeCache, srcBuf, output)) {
1216 return nullptr;
1218 return output.as<OutputType>().forget();
1221 already_AddRefed<CompilationStencil> frontend::ParseModuleToStencil(
1222 JSContext* maybeCx, FrontendContext* fc, js::LifoAlloc& tempLifoAlloc,
1223 CompilationInput& input, ScopeBindingCache* scopeCache,
1224 SourceText<char16_t>& srcBuf) {
1225 return ParseModuleToStencilImpl(maybeCx, fc, tempLifoAlloc, input, scopeCache,
1226 srcBuf);
1229 already_AddRefed<CompilationStencil> frontend::ParseModuleToStencil(
1230 JSContext* maybeCx, FrontendContext* fc, js::LifoAlloc& tempLifoAlloc,
1231 CompilationInput& input, ScopeBindingCache* scopeCache,
1232 SourceText<Utf8Unit>& srcBuf) {
1233 return ParseModuleToStencilImpl(maybeCx, fc, tempLifoAlloc, input, scopeCache,
1234 srcBuf);
1237 template <typename Unit>
1238 UniquePtr<ExtensibleCompilationStencil> ParseModuleToExtensibleStencilImpl(
1239 JSContext* cx, FrontendContext* fc, js::LifoAlloc& tempLifoAlloc,
1240 CompilationInput& input, ScopeBindingCache* scopeCache,
1241 SourceText<Unit>& srcBuf) {
1242 using OutputType = UniquePtr<ExtensibleCompilationStencil>;
1243 BytecodeCompilerOutput output((OutputType()));
1244 if (!ParseModuleToStencilAndMaybeInstantiate(cx, fc, tempLifoAlloc, input,
1245 scopeCache, srcBuf, output)) {
1246 return nullptr;
1248 return std::move(output.as<OutputType>());
1251 UniquePtr<ExtensibleCompilationStencil>
1252 frontend::ParseModuleToExtensibleStencil(JSContext* cx, FrontendContext* fc,
1253 js::LifoAlloc& tempLifoAlloc,
1254 CompilationInput& input,
1255 ScopeBindingCache* scopeCache,
1256 SourceText<char16_t>& srcBuf) {
1257 return ParseModuleToExtensibleStencilImpl(cx, fc, tempLifoAlloc, input,
1258 scopeCache, srcBuf);
1261 UniquePtr<ExtensibleCompilationStencil>
1262 frontend::ParseModuleToExtensibleStencil(JSContext* cx, FrontendContext* fc,
1263 js::LifoAlloc& tempLifoAlloc,
1264 CompilationInput& input,
1265 ScopeBindingCache* scopeCache,
1266 SourceText<Utf8Unit>& srcBuf) {
1267 return ParseModuleToExtensibleStencilImpl(cx, fc, tempLifoAlloc, input,
1268 scopeCache, srcBuf);
1271 template <typename Unit>
1272 static ModuleObject* CompileModuleImpl(
1273 JSContext* cx, FrontendContext* fc,
1274 const JS::ReadOnlyCompileOptions& optionsInput, SourceText<Unit>& srcBuf) {
1275 AutoAssertReportedException assertException(cx, fc);
1277 CompileOptions options(cx, optionsInput);
1278 options.setModule();
1280 Rooted<CompilationInput> input(cx, CompilationInput(options));
1281 Rooted<CompilationGCOutput> gcOutput(cx);
1282 BytecodeCompilerOutput output(gcOutput.address());
1284 NoScopeBindingCache scopeCache;
1285 if (!ParseModuleToStencilAndMaybeInstantiate(cx, fc, cx->tempLifoAlloc(),
1286 input.get(), &scopeCache, srcBuf,
1287 output)) {
1288 return nullptr;
1291 assertException.reset();
1292 return gcOutput.get().module;
1295 ModuleObject* frontend::CompileModule(JSContext* cx, FrontendContext* fc,
1296 const JS::ReadOnlyCompileOptions& options,
1297 SourceText<char16_t>& srcBuf) {
1298 return CompileModuleImpl(cx, fc, options, srcBuf);
1301 ModuleObject* frontend::CompileModule(JSContext* cx, FrontendContext* fc,
1302 const JS::ReadOnlyCompileOptions& options,
1303 SourceText<Utf8Unit>& srcBuf) {
1304 return CompileModuleImpl(cx, fc, options, srcBuf);
1307 static bool InstantiateLazyFunction(JSContext* cx, CompilationInput& input,
1308 CompilationStencil& stencil,
1309 BytecodeCompilerOutput& output) {
1310 // We do check the type, but do not write anything to it as this is not
1311 // necessary for lazy function, as the script is patched inside the
1312 // JSFunction when instantiating.
1313 MOZ_ASSERT(output.is<CompilationGCOutput*>());
1314 MOZ_ASSERT(!output.as<CompilationGCOutput*>());
1316 mozilla::DebugOnly<uint32_t> lazyFlags =
1317 static_cast<uint32_t>(input.immutableFlags());
1319 Rooted<CompilationGCOutput> gcOutput(cx);
1321 if (input.source->hasEncoder()) {
1322 if (!input.source->addDelazificationToIncrementalEncoding(cx, stencil)) {
1323 return false;
1327 if (!CompilationStencil::instantiateStencils(cx, input, stencil,
1328 gcOutput.get())) {
1329 return false;
1332 // NOTE: After instantiation succeeds and bytecode is attached, the rest of
1333 // this operation should be infallible. Any failure during
1334 // delazification should restore the function back to a consistent
1335 // lazy state.
1337 MOZ_ASSERT(lazyFlags == gcOutput.get().script->immutableFlags());
1338 MOZ_ASSERT(gcOutput.get().script->outermostScope()->hasOnChain(
1339 ScopeKind::NonSyntactic) ==
1340 gcOutput.get().script->immutableFlags().hasFlag(
1341 JSScript::ImmutableFlags::HasNonSyntacticScope));
1343 return true;
1346 enum class GetCachedResult {
1347 // Similar to return false.
1348 Error,
1350 // We have not found any entry.
1351 NotFound,
1353 // We have found an entry, and set everything according to the desired
1354 // BytecodeCompilerOutput out-param.
1355 Found
1358 // When we have a cache hit, the addPtr out-param would evaluate to a true-ish
1359 // value.
1360 static GetCachedResult GetCachedLazyFunctionStencilMaybeInstantiate(
1361 JSContext* maybeCx, FrontendContext* fc, CompilationInput& input,
1362 BytecodeCompilerOutput& output) {
1363 RefPtr<CompilationStencil> stencil;
1365 DelazificationCache& cache = DelazificationCache::getSingleton();
1366 auto guard = cache.isSourceCached(input.source);
1367 if (!guard) {
1368 return GetCachedResult::NotFound;
1371 // Before releasing the guard, which is locking the cache, we increment the
1372 // reference counter such that we do not reclaim the CompilationStencil
1373 // while we are instantiating it.
1374 StencilContext key(input.source, input.extent());
1375 stencil = cache.lookup(guard, key);
1376 if (!stencil) {
1377 return GetCachedResult::NotFound;
1381 if (output.is<RefPtr<CompilationStencil>>()) {
1382 output.as<RefPtr<CompilationStencil>>() = stencil;
1383 return GetCachedResult::Found;
1386 if (output.is<UniquePtr<ExtensibleCompilationStencil>>()) {
1387 auto extensible =
1388 fc->getAllocator()->make_unique<ExtensibleCompilationStencil>(input);
1389 if (!extensible) {
1390 return GetCachedResult::Error;
1392 if (!extensible->cloneFrom(fc, *stencil)) {
1393 return GetCachedResult::Error;
1396 output.as<UniquePtr<ExtensibleCompilationStencil>>() =
1397 std::move(extensible);
1398 return GetCachedResult::Found;
1401 MOZ_ASSERT(maybeCx);
1403 if (!InstantiateLazyFunction(maybeCx, input, *stencil, output)) {
1404 return GetCachedResult::Error;
1407 return GetCachedResult::Found;
1410 template <typename Unit>
1411 static bool CompileLazyFunctionToStencilMaybeInstantiate(
1412 JSContext* maybeCx, FrontendContext* fc, js::LifoAlloc& tempLifoAlloc,
1413 CompilationInput& input, ScopeBindingCache* scopeCache, const Unit* units,
1414 size_t length, BytecodeCompilerOutput& output) {
1415 MOZ_ASSERT(input.source);
1417 AutoAssertReportedException assertException(maybeCx, fc);
1418 if (input.options.consumeDelazificationCache()) {
1419 auto res = GetCachedLazyFunctionStencilMaybeInstantiate(maybeCx, fc, input,
1420 output);
1421 switch (res) {
1422 case GetCachedResult::Error:
1423 return false;
1424 case GetCachedResult::Found:
1425 assertException.reset();
1426 return true;
1427 case GetCachedResult::NotFound:
1428 break;
1432 InheritThis inheritThis =
1433 input.functionFlags().isArrow() ? InheritThis::Yes : InheritThis::No;
1435 LifoAllocScope parserAllocScope(&tempLifoAlloc);
1436 CompilationState compilationState(fc, parserAllocScope, input);
1437 compilationState.setFunctionKey(input.extent());
1438 MOZ_ASSERT(!compilationState.isInitialStencil());
1439 if (!compilationState.init(fc, scopeCache, inheritThis)) {
1440 return false;
1443 Parser<FullParseHandler, Unit> parser(fc, input.options, units, length,
1444 /* foldConstants = */ true,
1445 compilationState,
1446 /* syntaxParser = */ nullptr);
1447 if (!parser.checkOptions()) {
1448 return false;
1451 FunctionNode* pn =
1452 parser
1453 .standaloneLazyFunction(input, input.extent().toStringStart,
1454 input.strict(), input.generatorKind(),
1455 input.asyncKind())
1456 .unwrapOr(nullptr);
1457 if (!pn) {
1458 return false;
1461 BytecodeEmitter bce(fc, &parser, pn->funbox(), compilationState,
1462 BytecodeEmitter::LazyFunction);
1463 if (!bce.init(pn->pn_pos)) {
1464 return false;
1467 if (!bce.emitFunctionScript(pn)) {
1468 return false;
1471 // NOTE: Only allow relazification if there was no lazy PrivateScriptData.
1472 // This excludes non-leaf functions and all script class constructors.
1473 bool hadLazyScriptData = input.hasPrivateScriptData();
1474 bool isRelazifiableAfterDelazify = input.isRelazifiable();
1475 if (isRelazifiableAfterDelazify && !hadLazyScriptData) {
1476 compilationState.scriptData[CompilationStencil::TopLevelIndex]
1477 .setAllowRelazify();
1480 if (input.options.checkDelazificationCache()) {
1481 using OutputType = RefPtr<CompilationStencil>;
1482 BytecodeCompilerOutput cached((OutputType()));
1483 auto res = GetCachedLazyFunctionStencilMaybeInstantiate(nullptr, fc, input,
1484 cached);
1485 if (res == GetCachedResult::Error) {
1486 return false;
1488 // Cached results might be removed by GCs.
1489 if (res == GetCachedResult::Found) {
1490 auto& concurrentSharedData = cached.as<OutputType>().get()->sharedData;
1491 auto concurrentData =
1492 concurrentSharedData.isSingle()
1493 ? concurrentSharedData.asSingle()->get()->immutableData()
1494 : concurrentSharedData.asBorrow()
1495 ->asSingle()
1496 ->get()
1497 ->immutableData();
1498 auto ondemandData =
1499 compilationState.sharedData.asSingle()->get()->immutableData();
1500 MOZ_RELEASE_ASSERT(concurrentData.Length() == ondemandData.Length(),
1501 "Non-deterministic stencils");
1502 for (size_t i = 0; i < concurrentData.Length(); i++) {
1503 MOZ_RELEASE_ASSERT(concurrentData[i] == ondemandData[i],
1504 "Non-deterministic stencils");
1509 if (output.is<UniquePtr<ExtensibleCompilationStencil>>()) {
1510 auto stencil =
1511 fc->getAllocator()->make_unique<ExtensibleCompilationStencil>(
1512 std::move(compilationState));
1513 if (!stencil) {
1514 return false;
1516 output.as<UniquePtr<ExtensibleCompilationStencil>>() = std::move(stencil);
1517 } else if (output.is<RefPtr<CompilationStencil>>()) {
1518 Maybe<AutoGeckoProfilerEntry> pseudoFrame;
1519 if (maybeCx) {
1520 pseudoFrame.emplace(maybeCx, "script emit",
1521 JS::ProfilingCategoryPair::JS_Parsing);
1524 auto extensibleStencil =
1525 fc->getAllocator()->make_unique<frontend::ExtensibleCompilationStencil>(
1526 std::move(compilationState));
1527 if (!extensibleStencil) {
1528 return false;
1531 RefPtr<CompilationStencil> stencil =
1532 fc->getAllocator()->new_<CompilationStencil>(
1533 std::move(extensibleStencil));
1534 if (!stencil) {
1535 return false;
1538 output.as<RefPtr<CompilationStencil>>() = std::move(stencil);
1539 } else {
1540 MOZ_ASSERT(maybeCx);
1541 BorrowingCompilationStencil borrowingStencil(compilationState);
1542 if (!InstantiateLazyFunction(maybeCx, input, borrowingStencil, output)) {
1543 return false;
1547 assertException.reset();
1548 return true;
1551 template <typename Unit>
1552 static bool DelazifyCanonicalScriptedFunctionImpl(JSContext* cx,
1553 FrontendContext* fc,
1554 ScopeBindingCache* scopeCache,
1555 JS::Handle<JSFunction*> fun,
1556 JS::Handle<BaseScript*> lazy,
1557 ScriptSource* ss) {
1558 MOZ_ASSERT(!lazy->hasBytecode(), "Script is already compiled!");
1559 MOZ_ASSERT(lazy->function() == fun);
1561 MOZ_DIAGNOSTIC_ASSERT(!fun->isGhost());
1563 AutoIncrementalTimer timer(cx->realm()->timers.delazificationTime);
1565 size_t sourceStart = lazy->sourceStart();
1566 size_t sourceLength = lazy->sourceEnd() - lazy->sourceStart();
1568 MOZ_ASSERT(ss->hasSourceText());
1570 // Parse and compile the script from source.
1571 UncompressedSourceCache::AutoHoldEntry holder;
1573 MOZ_ASSERT(ss->hasSourceType<Unit>());
1575 ScriptSource::PinnedUnits<Unit> units(cx, ss, holder, sourceStart,
1576 sourceLength);
1577 if (!units.get()) {
1578 return false;
1581 JS::CompileOptions options(cx);
1582 options.setMutedErrors(lazy->mutedErrors())
1583 .setFileAndLine(lazy->filename(), lazy->lineno())
1584 .setColumn(JS::ColumnNumberOneOrigin(lazy->column()))
1585 .setScriptSourceOffset(lazy->sourceStart())
1586 .setNoScriptRval(false)
1587 .setSelfHostingMode(false)
1588 .setEagerDelazificationStrategy(lazy->delazificationMode());
1590 Rooted<CompilationInput> input(cx, CompilationInput(options));
1591 input.get().initFromLazy(cx, lazy, ss);
1593 CompilationGCOutput* unusedGcOutput = nullptr;
1594 BytecodeCompilerOutput output(unusedGcOutput);
1595 return CompileLazyFunctionToStencilMaybeInstantiate(
1596 cx, fc, cx->tempLifoAlloc(), input.get(), scopeCache, units.get(),
1597 sourceLength, output);
1600 bool frontend::DelazifyCanonicalScriptedFunction(JSContext* cx,
1601 FrontendContext* fc,
1602 JS::Handle<JSFunction*> fun) {
1603 Maybe<AutoGeckoProfilerEntry> pseudoFrame;
1604 if (cx) {
1605 pseudoFrame.emplace(cx, "script delazify",
1606 JS::ProfilingCategoryPair::JS_Parsing);
1609 Rooted<BaseScript*> lazy(cx, fun->baseScript());
1610 ScriptSource* ss = lazy->scriptSource();
1611 ScopeBindingCache* scopeCache = &cx->caches().scopeCache;
1613 if (ss->hasSourceType<Utf8Unit>()) {
1614 // UTF-8 source text.
1615 return DelazifyCanonicalScriptedFunctionImpl<Utf8Unit>(cx, fc, scopeCache,
1616 fun, lazy, ss);
1619 MOZ_ASSERT(ss->hasSourceType<char16_t>());
1621 // UTF-16 source text.
1622 return DelazifyCanonicalScriptedFunctionImpl<char16_t>(cx, fc, scopeCache,
1623 fun, lazy, ss);
1626 template <typename Unit>
1627 static already_AddRefed<CompilationStencil>
1628 DelazifyCanonicalScriptedFunctionImpl(
1629 FrontendContext* fc, js::LifoAlloc& tempLifoAlloc,
1630 const JS::PrefableCompileOptions& prefableOptions,
1631 ScopeBindingCache* scopeCache, CompilationStencil& context,
1632 ScriptIndex scriptIndex, DelazifyFailureReason* failureReason) {
1633 ScriptStencilRef script{context, scriptIndex};
1634 const ScriptStencilExtra& extra = script.scriptExtra();
1636 #if defined(EARLY_BETA_OR_EARLIER) || defined(DEBUG)
1637 const ScriptStencil& data = script.scriptData();
1638 MOZ_ASSERT(!data.hasSharedData(), "Script is already compiled!");
1639 MOZ_DIAGNOSTIC_ASSERT(!data.isGhost());
1640 #endif
1642 size_t sourceStart = extra.extent.sourceStart;
1643 size_t sourceLength = extra.extent.sourceEnd - sourceStart;
1645 ScriptSource* ss = context.source;
1646 MOZ_ASSERT(ss->hasSourceText());
1648 MOZ_ASSERT(ss->hasSourceType<Unit>());
1650 ScriptSource::PinnedUnitsIfUncompressed<Unit> units(ss, sourceStart,
1651 sourceLength);
1652 if (!units.get()) {
1653 *failureReason = DelazifyFailureReason::Compressed;
1654 return nullptr;
1657 JS::CompileOptions options(prefableOptions);
1658 options.setMutedErrors(ss->mutedErrors())
1659 .setFileAndLine(ss->filename(), extra.extent.lineno)
1660 .setColumn(JS::ColumnNumberOneOrigin(extra.extent.column))
1661 .setScriptSourceOffset(sourceStart)
1662 .setNoScriptRval(false)
1663 .setSelfHostingMode(false);
1665 // CompilationInput initialized with initFromStencil only reference
1666 // information from the CompilationStencil context and the ref-counted
1667 // ScriptSource, which are both GC-free.
1668 JS_HAZ_NON_GC_POINTER CompilationInput input(options);
1669 input.initFromStencil(context, scriptIndex, ss);
1671 using OutputType = RefPtr<CompilationStencil>;
1672 BytecodeCompilerOutput output((OutputType()));
1673 if (!CompileLazyFunctionToStencilMaybeInstantiate(
1674 nullptr, fc, tempLifoAlloc, input, scopeCache, units.get(),
1675 sourceLength, output)) {
1676 *failureReason = DelazifyFailureReason::Other;
1677 return nullptr;
1679 return output.as<OutputType>().forget();
1682 already_AddRefed<CompilationStencil>
1683 frontend::DelazifyCanonicalScriptedFunction(
1684 FrontendContext* fc, js::LifoAlloc& tempLifoAlloc,
1685 const JS::PrefableCompileOptions& prefableOptions,
1686 ScopeBindingCache* scopeCache, CompilationStencil& context,
1687 ScriptIndex scriptIndex, DelazifyFailureReason* failureReason) {
1688 ScriptSource* ss = context.source;
1689 if (ss->hasSourceType<Utf8Unit>()) {
1690 // UTF-8 source text.
1691 return DelazifyCanonicalScriptedFunctionImpl<Utf8Unit>(
1692 fc, tempLifoAlloc, prefableOptions, scopeCache, context, scriptIndex,
1693 failureReason);
1696 // UTF-16 source text.
1697 MOZ_ASSERT(ss->hasSourceType<char16_t>());
1698 return DelazifyCanonicalScriptedFunctionImpl<char16_t>(
1699 fc, tempLifoAlloc, prefableOptions, scopeCache, context, scriptIndex,
1700 failureReason);
1703 static JSFunction* CompileStandaloneFunction(
1704 JSContext* cx, const JS::ReadOnlyCompileOptions& options,
1705 JS::SourceText<char16_t>& srcBuf, const Maybe<uint32_t>& parameterListEnd,
1706 FunctionSyntaxKind syntaxKind, GeneratorKind generatorKind,
1707 FunctionAsyncKind asyncKind, JS::Handle<Scope*> enclosingScope = nullptr) {
1708 JS::Rooted<JSFunction*> fun(cx);
1710 AutoReportFrontendContext fc(cx);
1711 AutoAssertReportedException assertException(cx, &fc);
1713 Rooted<CompilationInput> input(cx, CompilationInput(options));
1714 if (enclosingScope) {
1715 if (!input.get().initForStandaloneFunctionInNonSyntacticScope(
1716 &fc, enclosingScope)) {
1717 return nullptr;
1719 } else {
1720 if (!input.get().initForStandaloneFunction(cx, &fc)) {
1721 return nullptr;
1725 LifoAllocScope parserAllocScope(&cx->tempLifoAlloc());
1726 InheritThis inheritThis = (syntaxKind == FunctionSyntaxKind::Arrow)
1727 ? InheritThis::Yes
1728 : InheritThis::No;
1729 ScopeBindingCache* scopeCache = &cx->caches().scopeCache;
1730 StandaloneFunctionCompiler<char16_t> compiler(&fc, parserAllocScope,
1731 input.get(), srcBuf);
1732 if (!compiler.init(&fc, scopeCache, inheritThis)) {
1733 return nullptr;
1736 if (!compiler.compile(cx, syntaxKind, generatorKind, asyncKind,
1737 parameterListEnd)) {
1738 return nullptr;
1741 Rooted<CompilationGCOutput> gcOutput(cx);
1742 RefPtr<ScriptSource> source;
1744 BorrowingCompilationStencil borrowingStencil(compiler.stencil());
1745 if (!CompilationStencil::instantiateStencils(
1746 cx, input.get(), borrowingStencil, gcOutput.get())) {
1747 return nullptr;
1749 source = borrowingStencil.source;
1752 fun = gcOutput.get().getFunctionNoBaseIndex(
1753 CompilationStencil::TopLevelIndex);
1754 MOZ_ASSERT(fun->hasBytecode() || IsAsmJSModule(fun));
1756 // Enqueue an off-thread source compression task after finishing parsing.
1757 if (!source->tryCompressOffThread(cx)) {
1758 return nullptr;
1761 // Note: If AsmJS successfully compiles, the into.script will still be
1762 // nullptr. In this case we have compiled to a native function instead of an
1763 // interpreted script.
1764 if (gcOutput.get().script) {
1765 if (parameterListEnd) {
1766 source->setParameterListEnd(*parameterListEnd);
1769 const JS::InstantiateOptions instantiateOptions(options);
1770 Rooted<JSScript*> script(cx, gcOutput.get().script);
1771 FireOnNewScript(cx, instantiateOptions, script);
1774 assertException.reset();
1776 return fun;
1779 JSFunction* frontend::CompileStandaloneFunction(
1780 JSContext* cx, const JS::ReadOnlyCompileOptions& options,
1781 JS::SourceText<char16_t>& srcBuf, const Maybe<uint32_t>& parameterListEnd,
1782 FunctionSyntaxKind syntaxKind) {
1783 return CompileStandaloneFunction(cx, options, srcBuf, parameterListEnd,
1784 syntaxKind, GeneratorKind::NotGenerator,
1785 FunctionAsyncKind::SyncFunction);
1788 JSFunction* frontend::CompileStandaloneGenerator(
1789 JSContext* cx, const JS::ReadOnlyCompileOptions& options,
1790 JS::SourceText<char16_t>& srcBuf, const Maybe<uint32_t>& parameterListEnd,
1791 FunctionSyntaxKind syntaxKind) {
1792 return CompileStandaloneFunction(cx, options, srcBuf, parameterListEnd,
1793 syntaxKind, GeneratorKind::Generator,
1794 FunctionAsyncKind::SyncFunction);
1797 JSFunction* frontend::CompileStandaloneAsyncFunction(
1798 JSContext* cx, const ReadOnlyCompileOptions& options,
1799 JS::SourceText<char16_t>& srcBuf, const Maybe<uint32_t>& parameterListEnd,
1800 FunctionSyntaxKind syntaxKind) {
1801 return CompileStandaloneFunction(cx, options, srcBuf, parameterListEnd,
1802 syntaxKind, GeneratorKind::NotGenerator,
1803 FunctionAsyncKind::AsyncFunction);
1806 JSFunction* frontend::CompileStandaloneAsyncGenerator(
1807 JSContext* cx, const ReadOnlyCompileOptions& options,
1808 JS::SourceText<char16_t>& srcBuf, const Maybe<uint32_t>& parameterListEnd,
1809 FunctionSyntaxKind syntaxKind) {
1810 return CompileStandaloneFunction(cx, options, srcBuf, parameterListEnd,
1811 syntaxKind, GeneratorKind::Generator,
1812 FunctionAsyncKind::AsyncFunction);
1815 JSFunction* frontend::CompileStandaloneFunctionInNonSyntacticScope(
1816 JSContext* cx, const JS::ReadOnlyCompileOptions& options,
1817 JS::SourceText<char16_t>& srcBuf, const Maybe<uint32_t>& parameterListEnd,
1818 FunctionSyntaxKind syntaxKind, JS::Handle<Scope*> enclosingScope) {
1819 MOZ_ASSERT(enclosingScope);
1820 return CompileStandaloneFunction(cx, options, srcBuf, parameterListEnd,
1821 syntaxKind, GeneratorKind::NotGenerator,
1822 FunctionAsyncKind::SyncFunction,
1823 enclosingScope);