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
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
62 using namespace js::frontend
;
65 using mozilla::Utf8Unit
;
67 using JS::CompileOptions
;
68 using JS::ReadOnlyCompileOptions
;
71 // RAII class to check the frontend reports an exception when it fails to
73 class MOZ_RAII AutoAssertReportedException
{
80 explicit AutoAssertReportedException(JSContext
* maybeCx
, FrontendContext
* fc
)
81 : maybeCx_(maybeCx
), fc_(fc
), check_(true) {}
82 void reset() { check_
= false; }
83 ~AutoAssertReportedException() {
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()) {
94 // TODO: Remove this once JSContext is removed from frontend.
96 MOZ_ASSERT(maybeCx_
->isExceptionPending() || fc_
->hadErrors());
98 MOZ_ASSERT(fc_
->hadErrors());
103 explicit AutoAssertReportedException(JSContext
*, FrontendContext
*) {}
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
{
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
>;
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
)) {
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
);
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
>;
178 using Base::compilationState_
;
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
;
190 explicit ScriptCompiler(FrontendContext
* fc
, LifoAllocScope
& parserAllocScope
,
191 CompilationInput
& input
,
192 SourceText
<Unit
>& sourceBuffer
)
193 : Base(fc
, parserAllocScope
, input
, sourceBuffer
) {}
198 [[nodiscard
]] bool compile(JSContext
* cx
, SharedContext
* sc
);
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()) {
215 JSRuntime
* rt
= cx
->runtime();
216 if (!Smoosh::tryCompileGlobalScriptToExtensibleStencil(cx
, fc
, input
, srcBuf
,
221 if (cx
->options().trackNotImplemented()) {
223 rt
->parserWatcherFile
.put("1");
225 rt
->parserWatcherFile
.put("0");
230 fprintf(stderr
, "Falling back!\n");
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
);
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
265 UniquePtr
<ExtensibleCompilationStencil
> extensibleStencil
;
266 if (!TrySmoosh(maybeCx
, fc
, input
, srcBuf
, extensibleStencil
)) {
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
));
293 output
.as
<RefPtr
<CompilationStencil
>>() = std::move(stencil
);
295 BorrowingCompilationStencil
borrowingStencil(*extensibleStencil
);
296 if (!InstantiateStencils(maybeCx
, input
, borrowingStencil
,
297 *(output
.as
<CompilationGCOutput
*>()))) {
304 #endif // JS_ENABLE_SMOOSH
306 if (input
.options
.selfHostingMode
) {
307 if (!input
.initForSelfHostingGlobal(fc
)) {
310 } else if (maybeExtraBindings
) {
311 if (!input
.initForGlobalWithExtraBindings(fc
, maybeExtraBindings
)) {
315 if (!input
.initForGlobal(fc
)) {
320 AutoAssertReportedException
assertException(maybeCx
, fc
);
322 LifoAllocScope
parserAllocScope(&tempLifoAlloc
);
323 ScriptCompiler
<Unit
> compiler(fc
, parserAllocScope
, input
, srcBuf
);
324 if (!compiler
.init(fc
, scopeCache
)) {
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
)) {
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
352 if (input
.options
.waitForDelazificationCache() && maybeCx
) {
353 WaitForAllDelazifyTasks(maybeCx
->runtime());
357 if (output
.is
<UniquePtr
<ExtensibleCompilationStencil
>>()) {
359 fc
->getAllocator()->make_unique
<ExtensibleCompilationStencil
>(
360 std::move(compiler
.stencil()));
364 output
.as
<UniquePtr
<ExtensibleCompilationStencil
>>() = std::move(stencil
);
365 } else if (output
.is
<RefPtr
<CompilationStencil
>>()) {
366 Maybe
<AutoGeckoProfilerEntry
> pseudoFrame
;
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
) {
379 RefPtr
<CompilationStencil
> stencil
=
380 fc
->getAllocator()->new_
<CompilationStencil
>(
381 std::move(extensibleStencil
));
386 output
.as
<RefPtr
<CompilationStencil
>>() = std::move(stencil
);
389 BorrowingCompilationStencil
borrowingStencil(compiler
.stencil());
390 if (!InstantiateStencils(maybeCx
, input
, borrowingStencil
,
391 *(output
.as
<CompilationGCOutput
*>()))) {
396 assertException
.reset();
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
)) {
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
,
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
)) {
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
,
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
,
488 // Enqueue an off-thread source compression task after finishing parsing.
489 if (!stencil
.source
->tryCompressOffThread(cx
)) {
493 Rooted
<JSScript
*> script(cx
, gcOutput
.script
);
494 const JS::InstantiateOptions
instantiateOptions(input
.options
);
495 FireOnNewScript(cx
, instantiateOptions
, script
);
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
)) {
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
,
525 static bool CreateExtraBindingInfoVector(
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
);
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.");
545 JS::Rooted
<JSString
*> str(cx
, unwrappedBindingKeys
[i
].toString());
547 UniqueChars utf8chars
= JS_EncodeStringToUTF8(cx
, str
);
552 bool isShadowed
= false;
554 id
= unwrappedBindingKeys
[i
];
558 if (!HasProperty(cx
, cx
->global(), id
, &found
)) {
564 if (!HasProperty(cx
, globalLexical
, id
, &found
)) {
572 extraBindings
.infallibleEmplaceBack(std::move(utf8chars
), isShadowed
);
578 static WithEnvironmentObject
* CreateExtraBindingsEnvironment(
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
) {
589 MOZ_ASSERT(unwrappedBindingKeys
.length() == extraBindings
.length());
591 JS::Rooted
<JS::PropertyKey
> id(cx
);
593 for (const auto& bindingInfo
: extraBindings
) {
594 if (bindingInfo
.isShadowed
) {
599 id
= unwrappedBindingKeys
[i
];
601 JS::Rooted
<JS::Value
> val(cx
, unwrappedBindingValues
[i
]);
602 if (!cx
->compartment()->wrap(cx
, &val
) ||
603 !NativeDefineDataProperty(cx
, extraBindingsObj
, id
, val
, 0)) {
609 // The list of bindings shouldn't be modified.
610 if (!SetIntegrityLevel(cx
, extraBindingsObj
, IntegrityLevel::Sealed
)) {
614 JS::Rooted
<JSObject
*> globalLexical(cx
, &cx
->global()->lexicalEnvironment());
615 return WithEnvironmentObject::createNonSyntactic(cx
, extraBindingsObj
,
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
)) {
631 JS::Rooted
<JSScript
*> script(
632 cx
, CompileGlobalScriptImpl(cx
, fc
, options
, srcBuf
,
633 ScopeKind::NonSyntactic
, &extraBindings
));
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.
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
,
659 WithEnvironmentObject
* extraBindingsEnv
= CreateExtraBindingsEnvironment(
660 cx
, unwrappedBindingKeys
, unwrappedBindingValues
, extraBindings
);
661 if (!extraBindingsEnv
) {
665 env
.set(extraBindingsEnv
);
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
,
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
)) {
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
)) {
701 uint32_t len
= srcBuf
.length();
702 SourceExtent extent
= SourceExtent::makeGlobalExtent(
704 JS::LimitedColumnNumberOneOrigin::fromUnlimited(
705 JS::ColumnNumberOneOrigin(options
.column
)));
706 EvalSharedContext
evalsc(&fc
, compiler
.compilationState(), extent
);
707 if (!compiler
.compile(cx
, &evalsc
)) {
711 Rooted
<CompilationGCOutput
> gcOutput(cx
);
713 BorrowingCompilationStencil
borrowingStencil(compiler
.stencil());
714 if (!InstantiateStencils(cx
, input
.get(), borrowingStencil
,
720 assertException
.reset();
721 script
= gcOutput
.get().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
,
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
;
745 explicit ModuleCompiler(FrontendContext
* fc
, LifoAllocScope
& parserAllocScope
,
746 CompilationInput
& input
,
747 SourceText
<Unit
>& sourceBuffer
)
748 : Base(fc
, parserAllocScope
, input
, sourceBuffer
) {}
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
;
767 using Base::sourceBuffer_
;
769 using typename
Base::TokenStreamPosition
;
772 explicit StandaloneFunctionCompiler(FrontendContext
* fc
,
773 LifoAllocScope
& parserAllocScope
,
774 CompilationInput
& input
,
775 SourceText
<Unit
>& sourceBuffer
)
776 : Base(fc
, parserAllocScope
, input
, sourceBuffer
) {}
782 FunctionNode
* parse(JSContext
* cx
, FunctionSyntaxKind syntaxKind
,
783 GeneratorKind generatorKind
, FunctionAsyncKind asyncKind
,
784 const Maybe
<uint32_t>& parameterListEnd
);
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
;
799 if (!compilationState_
.source
->assignSource(fc
, options
, sourceBuffer_
)) {
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()) {
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.
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
) {
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
) {
877 const auto& nameInfo
= item
.value();
878 if (nameInfo
.empty()) {
882 // This name is free, and uses the extra binding.
890 template <typename Unit
>
891 bool ScriptCompiler
<Unit
>::popupateExtraBindingsFields(
892 GlobalSharedContext
* globalsc
) {
893 if (!compilationState_
.input
.internExtraBindings(
894 this->fc_
, compilationState_
.parserAtoms
)) {
898 bool hasNonShadowedBinding
= false;
899 for (auto& bindingInfo
: compilationState_
.input
.extraBindings()) {
900 if (bindingInfo
.isShadowed
) {
904 bool isShadowed
= false;
906 if (globalsc
->bindings
) {
907 for (ParserBindingIter
bi(*globalsc
->bindings
); bi
; bi
++) {
908 if (bindingInfo
.nameIndex
== bi
.name()) {
915 bindingInfo
.isShadowed
= isShadowed
;
917 hasNonShadowedBinding
= true;
921 if (!hasNonShadowedBinding
) {
922 // All bindings are shadowed.
923 this->fc_
->reportExtraBindingsAreNotUsed();
927 if (globalsc
->hasDirectEval()) {
928 // Direct eval can contain reference.
932 if (!UsesExtraBindings(globalsc
, compilationState_
.input
.extraBindings(),
933 parser
->usedNames().map())) {
934 this->fc_
->reportExtraBindingsAreNotUsed();
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_
)) {
956 Maybe
<AutoGeckoProfilerEntry
> pseudoFrame
;
958 pseudoFrame
.emplace(maybeCx
, "script parsing",
959 JS::ProfilingCategoryPair::JS_Parsing
);
961 if (sc
->isEvalContext()) {
962 pn
= parser
->evalBody(sc
->asEvalContext()).unwrapOr(nullptr);
964 pn
= parser
->globalBody(sc
->asGlobalContext()).unwrapOr(nullptr);
969 // Global and eval scripts don't get reparsed after a new directive was
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
));
977 if (sc
->isGlobalContext() && compilationState_
.input
.hasExtraBindings()) {
978 if (!popupateExtraBindingsFields(sc
->asGlobalContext())) {
984 // Successfully parsed. Emit the script.
985 Maybe
<AutoGeckoProfilerEntry
> pseudoFrame
;
987 pseudoFrame
.emplace(maybeCx
, "script emit",
988 JS::ProfilingCategoryPair::JS_Parsing
);
991 Maybe
<BytecodeEmitter
> emitter
;
992 if (!emplaceEmitter(emitter
, sc
)) {
996 if (!emitter
->emitScript(pn
)) {
1001 MOZ_ASSERT(!this->fc_
->hadErrors());
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
)) {
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);
1031 Maybe
<BytecodeEmitter
> emitter
;
1032 if (!emplaceEmitter(emitter
, &modulesc
)) {
1036 if (!emitter
->emitScript(pn
->as
<ModuleNode
>().body())) {
1040 StencilModuleMetadata
& moduleMetadata
= *compilationState_
.moduleMetadata
;
1042 builder
.finishFunctionDecls(moduleMetadata
);
1044 MOZ_ASSERT(!this->fc_
->hadErrors());
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()
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
1068 Directives newDirectives
= compilationState_
.directives
;
1070 ->standaloneFunction(parameterListEnd
, syntaxKind
, generatorKind
,
1071 asyncKind
, compilationState_
.directives
,
1078 // Maybe we encountered a new directive. See if we can try again.
1079 if (!canHandleParseFailure(newDirectives
)) {
1083 handleParseFailure(newDirectives
, startPosition
, startStatePosition
);
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
) {
1100 FunctionBox
* funbox
= parsedFunction
->funbox();
1102 if (funbox
->isInterpreted()) {
1103 Maybe
<BytecodeEmitter
> emitter
;
1104 if (!emplaceEmitter(emitter
, funbox
)) {
1108 if (!emitter
->emitFunctionScript(parsedFunction
)) {
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
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
,
1123 JS::LimitedColumnNumberOneOrigin::fromUnlimited(
1124 JS::ColumnNumberOneOrigin(options
.column
))};
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());
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
)) {
1152 AutoAssertReportedException
assertException(maybeCx
, fc
);
1154 LifoAllocScope
parserAllocScope(&tempLifoAlloc
);
1155 ModuleCompiler
<Unit
> compiler(fc
, parserAllocScope
, input
, srcBuf
);
1156 if (!compiler
.init(fc
, scopeCache
)) {
1160 if (!compiler
.compile(maybeCx
, fc
)) {
1164 if (output
.is
<UniquePtr
<ExtensibleCompilationStencil
>>()) {
1166 fc
->getAllocator()->make_unique
<ExtensibleCompilationStencil
>(
1167 std::move(compiler
.stencil()));
1171 output
.as
<UniquePtr
<ExtensibleCompilationStencil
>>() = std::move(stencil
);
1172 } else if (output
.is
<RefPtr
<CompilationStencil
>>()) {
1173 Maybe
<AutoGeckoProfilerEntry
> pseudoFrame
;
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
) {
1186 RefPtr
<CompilationStencil
> stencil
=
1187 fc
->getAllocator()->new_
<CompilationStencil
>(
1188 std::move(extensibleStencil
));
1193 output
.as
<RefPtr
<CompilationStencil
>>() = std::move(stencil
);
1195 MOZ_ASSERT(maybeCx
);
1196 BorrowingCompilationStencil
borrowingStencil(compiler
.stencil());
1197 if (!InstantiateStencils(maybeCx
, input
, borrowingStencil
,
1198 *(output
.as
<CompilationGCOutput
*>()))) {
1203 assertException
.reset();
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
)) {
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
,
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
,
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
)) {
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
,
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
)) {
1327 if (!CompilationStencil::instantiateStencils(cx
, input
, stencil
,
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
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
));
1346 enum class GetCachedResult
{
1347 // Similar to return false.
1350 // We have not found any entry.
1353 // We have found an entry, and set everything according to the desired
1354 // BytecodeCompilerOutput out-param.
1358 // When we have a cache hit, the addPtr out-param would evaluate to a true-ish
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
);
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
);
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
>>()) {
1388 fc
->getAllocator()->make_unique
<ExtensibleCompilationStencil
>(input
);
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
,
1422 case GetCachedResult::Error
:
1424 case GetCachedResult::Found
:
1425 assertException
.reset();
1427 case GetCachedResult::NotFound
:
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
)) {
1443 Parser
<FullParseHandler
, Unit
> parser(fc
, input
.options
, units
, length
,
1444 /* foldConstants = */ true,
1446 /* syntaxParser = */ nullptr);
1447 if (!parser
.checkOptions()) {
1453 .standaloneLazyFunction(input
, input
.extent().toStringStart
,
1454 input
.strict(), input
.generatorKind(),
1461 BytecodeEmitter
bce(fc
, &parser
, pn
->funbox(), compilationState
,
1462 BytecodeEmitter::LazyFunction
);
1463 if (!bce
.init(pn
->pn_pos
)) {
1467 if (!bce
.emitFunctionScript(pn
)) {
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
,
1485 if (res
== GetCachedResult::Error
) {
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()
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
>>()) {
1511 fc
->getAllocator()->make_unique
<ExtensibleCompilationStencil
>(
1512 std::move(compilationState
));
1516 output
.as
<UniquePtr
<ExtensibleCompilationStencil
>>() = std::move(stencil
);
1517 } else if (output
.is
<RefPtr
<CompilationStencil
>>()) {
1518 Maybe
<AutoGeckoProfilerEntry
> pseudoFrame
;
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
) {
1531 RefPtr
<CompilationStencil
> stencil
=
1532 fc
->getAllocator()->new_
<CompilationStencil
>(
1533 std::move(extensibleStencil
));
1538 output
.as
<RefPtr
<CompilationStencil
>>() = std::move(stencil
);
1540 MOZ_ASSERT(maybeCx
);
1541 BorrowingCompilationStencil
borrowingStencil(compilationState
);
1542 if (!InstantiateLazyFunction(maybeCx
, input
, borrowingStencil
, output
)) {
1547 assertException
.reset();
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
,
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
,
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
;
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
,
1619 MOZ_ASSERT(ss
->hasSourceType
<char16_t
>());
1621 // UTF-16 source text.
1622 return DelazifyCanonicalScriptedFunctionImpl
<char16_t
>(cx
, fc
, scopeCache
,
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());
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
,
1653 *failureReason
= DelazifyFailureReason::Compressed
;
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
;
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
,
1696 // UTF-16 source text.
1697 MOZ_ASSERT(ss
->hasSourceType
<char16_t
>());
1698 return DelazifyCanonicalScriptedFunctionImpl
<char16_t
>(
1699 fc
, tempLifoAlloc
, prefableOptions
, scopeCache
, context
, scriptIndex
,
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
)) {
1720 if (!input
.get().initForStandaloneFunction(cx
, &fc
)) {
1725 LifoAllocScope
parserAllocScope(&cx
->tempLifoAlloc());
1726 InheritThis inheritThis
= (syntaxKind
== FunctionSyntaxKind::Arrow
)
1729 ScopeBindingCache
* scopeCache
= &cx
->caches().scopeCache
;
1730 StandaloneFunctionCompiler
<char16_t
> compiler(&fc
, parserAllocScope
,
1731 input
.get(), srcBuf
);
1732 if (!compiler
.init(&fc
, scopeCache
, inheritThis
)) {
1736 if (!compiler
.compile(cx
, syntaxKind
, generatorKind
, asyncKind
,
1737 parameterListEnd
)) {
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())) {
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
)) {
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();
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
,