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());
1147 MOZ_ASSERT(input
.options
.lineno
!= 0,
1148 "Module cannot be compiled with lineNumber == 0");
1150 if (!input
.initForModule(fc
)) {
1154 AutoAssertReportedException
assertException(maybeCx
, fc
);
1156 LifoAllocScope
parserAllocScope(&tempLifoAlloc
);
1157 ModuleCompiler
<Unit
> compiler(fc
, parserAllocScope
, input
, srcBuf
);
1158 if (!compiler
.init(fc
, scopeCache
)) {
1162 if (!compiler
.compile(maybeCx
, fc
)) {
1166 if (output
.is
<UniquePtr
<ExtensibleCompilationStencil
>>()) {
1168 fc
->getAllocator()->make_unique
<ExtensibleCompilationStencil
>(
1169 std::move(compiler
.stencil()));
1173 output
.as
<UniquePtr
<ExtensibleCompilationStencil
>>() = std::move(stencil
);
1174 } else if (output
.is
<RefPtr
<CompilationStencil
>>()) {
1175 Maybe
<AutoGeckoProfilerEntry
> pseudoFrame
;
1177 pseudoFrame
.emplace(maybeCx
, "script emit",
1178 JS::ProfilingCategoryPair::JS_Parsing
);
1181 auto extensibleStencil
=
1182 fc
->getAllocator()->make_unique
<frontend::ExtensibleCompilationStencil
>(
1183 std::move(compiler
.stencil()));
1184 if (!extensibleStencil
) {
1188 RefPtr
<CompilationStencil
> stencil
=
1189 fc
->getAllocator()->new_
<CompilationStencil
>(
1190 std::move(extensibleStencil
));
1195 output
.as
<RefPtr
<CompilationStencil
>>() = std::move(stencil
);
1197 MOZ_ASSERT(maybeCx
);
1198 BorrowingCompilationStencil
borrowingStencil(compiler
.stencil());
1199 if (!InstantiateStencils(maybeCx
, input
, borrowingStencil
,
1200 *(output
.as
<CompilationGCOutput
*>()))) {
1205 assertException
.reset();
1209 template <typename Unit
>
1210 already_AddRefed
<CompilationStencil
> ParseModuleToStencilImpl(
1211 JSContext
* maybeCx
, FrontendContext
* fc
, js::LifoAlloc
& tempLifoAlloc
,
1212 CompilationInput
& input
, ScopeBindingCache
* scopeCache
,
1213 SourceText
<Unit
>& srcBuf
) {
1214 using OutputType
= RefPtr
<CompilationStencil
>;
1215 BytecodeCompilerOutput
output((OutputType()));
1216 if (!ParseModuleToStencilAndMaybeInstantiate(
1217 maybeCx
, fc
, tempLifoAlloc
, input
, scopeCache
, srcBuf
, output
)) {
1220 return output
.as
<OutputType
>().forget();
1223 already_AddRefed
<CompilationStencil
> frontend::ParseModuleToStencil(
1224 JSContext
* maybeCx
, FrontendContext
* fc
, js::LifoAlloc
& tempLifoAlloc
,
1225 CompilationInput
& input
, ScopeBindingCache
* scopeCache
,
1226 SourceText
<char16_t
>& srcBuf
) {
1227 return ParseModuleToStencilImpl(maybeCx
, fc
, tempLifoAlloc
, input
, scopeCache
,
1231 already_AddRefed
<CompilationStencil
> frontend::ParseModuleToStencil(
1232 JSContext
* maybeCx
, FrontendContext
* fc
, js::LifoAlloc
& tempLifoAlloc
,
1233 CompilationInput
& input
, ScopeBindingCache
* scopeCache
,
1234 SourceText
<Utf8Unit
>& srcBuf
) {
1235 return ParseModuleToStencilImpl(maybeCx
, fc
, tempLifoAlloc
, input
, scopeCache
,
1239 template <typename Unit
>
1240 UniquePtr
<ExtensibleCompilationStencil
> ParseModuleToExtensibleStencilImpl(
1241 JSContext
* cx
, FrontendContext
* fc
, js::LifoAlloc
& tempLifoAlloc
,
1242 CompilationInput
& input
, ScopeBindingCache
* scopeCache
,
1243 SourceText
<Unit
>& srcBuf
) {
1244 using OutputType
= UniquePtr
<ExtensibleCompilationStencil
>;
1245 BytecodeCompilerOutput
output((OutputType()));
1246 if (!ParseModuleToStencilAndMaybeInstantiate(cx
, fc
, tempLifoAlloc
, input
,
1247 scopeCache
, srcBuf
, output
)) {
1250 return std::move(output
.as
<OutputType
>());
1253 UniquePtr
<ExtensibleCompilationStencil
>
1254 frontend::ParseModuleToExtensibleStencil(JSContext
* cx
, FrontendContext
* fc
,
1255 js::LifoAlloc
& tempLifoAlloc
,
1256 CompilationInput
& input
,
1257 ScopeBindingCache
* scopeCache
,
1258 SourceText
<char16_t
>& srcBuf
) {
1259 return ParseModuleToExtensibleStencilImpl(cx
, fc
, tempLifoAlloc
, input
,
1260 scopeCache
, srcBuf
);
1263 UniquePtr
<ExtensibleCompilationStencil
>
1264 frontend::ParseModuleToExtensibleStencil(JSContext
* cx
, FrontendContext
* fc
,
1265 js::LifoAlloc
& tempLifoAlloc
,
1266 CompilationInput
& input
,
1267 ScopeBindingCache
* scopeCache
,
1268 SourceText
<Utf8Unit
>& srcBuf
) {
1269 return ParseModuleToExtensibleStencilImpl(cx
, fc
, tempLifoAlloc
, input
,
1270 scopeCache
, srcBuf
);
1273 template <typename Unit
>
1274 static ModuleObject
* CompileModuleImpl(
1275 JSContext
* cx
, FrontendContext
* fc
,
1276 const JS::ReadOnlyCompileOptions
& optionsInput
, SourceText
<Unit
>& srcBuf
) {
1277 AutoAssertReportedException
assertException(cx
, fc
);
1279 CompileOptions
options(cx
, optionsInput
);
1280 options
.setModule();
1282 Rooted
<CompilationInput
> input(cx
, CompilationInput(options
));
1283 Rooted
<CompilationGCOutput
> gcOutput(cx
);
1284 BytecodeCompilerOutput
output(gcOutput
.address());
1286 NoScopeBindingCache scopeCache
;
1287 if (!ParseModuleToStencilAndMaybeInstantiate(cx
, fc
, cx
->tempLifoAlloc(),
1288 input
.get(), &scopeCache
, srcBuf
,
1293 assertException
.reset();
1294 return gcOutput
.get().module
;
1297 ModuleObject
* frontend::CompileModule(JSContext
* cx
, FrontendContext
* fc
,
1298 const JS::ReadOnlyCompileOptions
& options
,
1299 SourceText
<char16_t
>& srcBuf
) {
1300 return CompileModuleImpl(cx
, fc
, options
, srcBuf
);
1303 ModuleObject
* frontend::CompileModule(JSContext
* cx
, FrontendContext
* fc
,
1304 const JS::ReadOnlyCompileOptions
& options
,
1305 SourceText
<Utf8Unit
>& srcBuf
) {
1306 return CompileModuleImpl(cx
, fc
, options
, srcBuf
);
1309 static bool InstantiateLazyFunction(JSContext
* cx
, CompilationInput
& input
,
1310 CompilationStencil
& stencil
,
1311 BytecodeCompilerOutput
& output
) {
1312 // We do check the type, but do not write anything to it as this is not
1313 // necessary for lazy function, as the script is patched inside the
1314 // JSFunction when instantiating.
1315 MOZ_ASSERT(output
.is
<CompilationGCOutput
*>());
1316 MOZ_ASSERT(!output
.as
<CompilationGCOutput
*>());
1318 mozilla::DebugOnly
<uint32_t> lazyFlags
=
1319 static_cast<uint32_t>(input
.immutableFlags());
1321 Rooted
<CompilationGCOutput
> gcOutput(cx
);
1323 if (input
.source
->hasEncoder()) {
1324 if (!input
.source
->addDelazificationToIncrementalEncoding(cx
, stencil
)) {
1329 if (!CompilationStencil::instantiateStencils(cx
, input
, stencil
,
1334 // NOTE: After instantiation succeeds and bytecode is attached, the rest of
1335 // this operation should be infallible. Any failure during
1336 // delazification should restore the function back to a consistent
1339 MOZ_ASSERT(lazyFlags
== gcOutput
.get().script
->immutableFlags());
1340 MOZ_ASSERT(gcOutput
.get().script
->outermostScope()->hasOnChain(
1341 ScopeKind::NonSyntactic
) ==
1342 gcOutput
.get().script
->immutableFlags().hasFlag(
1343 JSScript::ImmutableFlags::HasNonSyntacticScope
));
1348 enum class GetCachedResult
{
1349 // Similar to return false.
1352 // We have not found any entry.
1355 // We have found an entry, and set everything according to the desired
1356 // BytecodeCompilerOutput out-param.
1360 // When we have a cache hit, the addPtr out-param would evaluate to a true-ish
1362 static GetCachedResult
GetCachedLazyFunctionStencilMaybeInstantiate(
1363 JSContext
* maybeCx
, FrontendContext
* fc
, CompilationInput
& input
,
1364 BytecodeCompilerOutput
& output
) {
1365 RefPtr
<CompilationStencil
> stencil
;
1367 DelazificationCache
& cache
= DelazificationCache::getSingleton();
1368 auto guard
= cache
.isSourceCached(input
.source
);
1370 return GetCachedResult::NotFound
;
1373 // Before releasing the guard, which is locking the cache, we increment the
1374 // reference counter such that we do not reclaim the CompilationStencil
1375 // while we are instantiating it.
1376 StencilContext
key(input
.source
, input
.extent());
1377 stencil
= cache
.lookup(guard
, key
);
1379 return GetCachedResult::NotFound
;
1383 if (output
.is
<RefPtr
<CompilationStencil
>>()) {
1384 output
.as
<RefPtr
<CompilationStencil
>>() = stencil
;
1385 return GetCachedResult::Found
;
1388 if (output
.is
<UniquePtr
<ExtensibleCompilationStencil
>>()) {
1390 fc
->getAllocator()->make_unique
<ExtensibleCompilationStencil
>(input
);
1392 return GetCachedResult::Error
;
1394 if (!extensible
->cloneFrom(fc
, *stencil
)) {
1395 return GetCachedResult::Error
;
1398 output
.as
<UniquePtr
<ExtensibleCompilationStencil
>>() =
1399 std::move(extensible
);
1400 return GetCachedResult::Found
;
1403 MOZ_ASSERT(maybeCx
);
1405 if (!InstantiateLazyFunction(maybeCx
, input
, *stencil
, output
)) {
1406 return GetCachedResult::Error
;
1409 return GetCachedResult::Found
;
1412 template <typename Unit
>
1413 static bool CompileLazyFunctionToStencilMaybeInstantiate(
1414 JSContext
* maybeCx
, FrontendContext
* fc
, js::LifoAlloc
& tempLifoAlloc
,
1415 CompilationInput
& input
, ScopeBindingCache
* scopeCache
, const Unit
* units
,
1416 size_t length
, BytecodeCompilerOutput
& output
) {
1417 MOZ_ASSERT(input
.source
);
1419 AutoAssertReportedException
assertException(maybeCx
, fc
);
1420 if (input
.options
.consumeDelazificationCache()) {
1421 auto res
= GetCachedLazyFunctionStencilMaybeInstantiate(maybeCx
, fc
, input
,
1424 case GetCachedResult::Error
:
1426 case GetCachedResult::Found
:
1427 assertException
.reset();
1429 case GetCachedResult::NotFound
:
1434 InheritThis inheritThis
=
1435 input
.functionFlags().isArrow() ? InheritThis::Yes
: InheritThis::No
;
1437 LifoAllocScope
parserAllocScope(&tempLifoAlloc
);
1438 CompilationState
compilationState(fc
, parserAllocScope
, input
);
1439 compilationState
.setFunctionKey(input
.extent());
1440 MOZ_ASSERT(!compilationState
.isInitialStencil());
1441 if (!compilationState
.init(fc
, scopeCache
, inheritThis
)) {
1445 Parser
<FullParseHandler
, Unit
> parser(fc
, input
.options
, units
, length
,
1446 /* foldConstants = */ true,
1448 /* syntaxParser = */ nullptr);
1449 if (!parser
.checkOptions()) {
1455 .standaloneLazyFunction(input
, input
.extent().toStringStart
,
1456 input
.strict(), input
.generatorKind(),
1463 BytecodeEmitter
bce(fc
, &parser
, pn
->funbox(), compilationState
,
1464 BytecodeEmitter::LazyFunction
);
1465 if (!bce
.init(pn
->pn_pos
)) {
1469 if (!bce
.emitFunctionScript(pn
)) {
1473 // NOTE: Only allow relazification if there was no lazy PrivateScriptData.
1474 // This excludes non-leaf functions and all script class constructors.
1475 bool hadLazyScriptData
= input
.hasPrivateScriptData();
1476 bool isRelazifiableAfterDelazify
= input
.isRelazifiable();
1477 if (isRelazifiableAfterDelazify
&& !hadLazyScriptData
) {
1478 compilationState
.scriptData
[CompilationStencil::TopLevelIndex
]
1479 .setAllowRelazify();
1482 if (input
.options
.checkDelazificationCache()) {
1483 using OutputType
= RefPtr
<CompilationStencil
>;
1484 BytecodeCompilerOutput
cached((OutputType()));
1485 auto res
= GetCachedLazyFunctionStencilMaybeInstantiate(nullptr, fc
, input
,
1487 if (res
== GetCachedResult::Error
) {
1490 // Cached results might be removed by GCs.
1491 if (res
== GetCachedResult::Found
) {
1492 auto& concurrentSharedData
= cached
.as
<OutputType
>().get()->sharedData
;
1493 auto concurrentData
=
1494 concurrentSharedData
.isSingle()
1495 ? concurrentSharedData
.asSingle()->get()->immutableData()
1496 : concurrentSharedData
.asBorrow()
1501 compilationState
.sharedData
.asSingle()->get()->immutableData();
1502 MOZ_RELEASE_ASSERT(concurrentData
.Length() == ondemandData
.Length(),
1503 "Non-deterministic stencils");
1504 for (size_t i
= 0; i
< concurrentData
.Length(); i
++) {
1505 MOZ_RELEASE_ASSERT(concurrentData
[i
] == ondemandData
[i
],
1506 "Non-deterministic stencils");
1511 if (output
.is
<UniquePtr
<ExtensibleCompilationStencil
>>()) {
1513 fc
->getAllocator()->make_unique
<ExtensibleCompilationStencil
>(
1514 std::move(compilationState
));
1518 output
.as
<UniquePtr
<ExtensibleCompilationStencil
>>() = std::move(stencil
);
1519 } else if (output
.is
<RefPtr
<CompilationStencil
>>()) {
1520 Maybe
<AutoGeckoProfilerEntry
> pseudoFrame
;
1522 pseudoFrame
.emplace(maybeCx
, "script emit",
1523 JS::ProfilingCategoryPair::JS_Parsing
);
1526 auto extensibleStencil
=
1527 fc
->getAllocator()->make_unique
<frontend::ExtensibleCompilationStencil
>(
1528 std::move(compilationState
));
1529 if (!extensibleStencil
) {
1533 RefPtr
<CompilationStencil
> stencil
=
1534 fc
->getAllocator()->new_
<CompilationStencil
>(
1535 std::move(extensibleStencil
));
1540 output
.as
<RefPtr
<CompilationStencil
>>() = std::move(stencil
);
1542 MOZ_ASSERT(maybeCx
);
1543 BorrowingCompilationStencil
borrowingStencil(compilationState
);
1544 if (!InstantiateLazyFunction(maybeCx
, input
, borrowingStencil
, output
)) {
1549 assertException
.reset();
1553 template <typename Unit
>
1554 static bool DelazifyCanonicalScriptedFunctionImpl(JSContext
* cx
,
1555 FrontendContext
* fc
,
1556 ScopeBindingCache
* scopeCache
,
1557 JS::Handle
<JSFunction
*> fun
,
1558 JS::Handle
<BaseScript
*> lazy
,
1560 MOZ_ASSERT(!lazy
->hasBytecode(), "Script is already compiled!");
1561 MOZ_ASSERT(lazy
->function() == fun
);
1563 MOZ_DIAGNOSTIC_ASSERT(!fun
->isGhost());
1565 AutoIncrementalTimer
timer(cx
->realm()->timers
.delazificationTime
);
1567 size_t sourceStart
= lazy
->sourceStart();
1568 size_t sourceLength
= lazy
->sourceEnd() - lazy
->sourceStart();
1570 MOZ_ASSERT(ss
->hasSourceText());
1572 // Parse and compile the script from source.
1573 UncompressedSourceCache::AutoHoldEntry holder
;
1575 MOZ_ASSERT(ss
->hasSourceType
<Unit
>());
1577 ScriptSource::PinnedUnits
<Unit
> units(cx
, ss
, holder
, sourceStart
,
1583 JS::CompileOptions
options(cx
);
1584 options
.setMutedErrors(lazy
->mutedErrors())
1585 .setFileAndLine(lazy
->filename(), lazy
->lineno())
1586 .setColumn(JS::ColumnNumberOneOrigin(lazy
->column()))
1587 .setScriptSourceOffset(lazy
->sourceStart())
1588 .setNoScriptRval(false)
1589 .setSelfHostingMode(false)
1590 .setEagerDelazificationStrategy(lazy
->delazificationMode());
1592 Rooted
<CompilationInput
> input(cx
, CompilationInput(options
));
1593 input
.get().initFromLazy(cx
, lazy
, ss
);
1595 CompilationGCOutput
* unusedGcOutput
= nullptr;
1596 BytecodeCompilerOutput
output(unusedGcOutput
);
1597 return CompileLazyFunctionToStencilMaybeInstantiate(
1598 cx
, fc
, cx
->tempLifoAlloc(), input
.get(), scopeCache
, units
.get(),
1599 sourceLength
, output
);
1602 bool frontend::DelazifyCanonicalScriptedFunction(JSContext
* cx
,
1603 FrontendContext
* fc
,
1604 JS::Handle
<JSFunction
*> fun
) {
1605 Maybe
<AutoGeckoProfilerEntry
> pseudoFrame
;
1607 pseudoFrame
.emplace(cx
, "script delazify",
1608 JS::ProfilingCategoryPair::JS_Parsing
);
1611 Rooted
<BaseScript
*> lazy(cx
, fun
->baseScript());
1612 ScriptSource
* ss
= lazy
->scriptSource();
1613 ScopeBindingCache
* scopeCache
= &cx
->caches().scopeCache
;
1615 if (ss
->hasSourceType
<Utf8Unit
>()) {
1616 // UTF-8 source text.
1617 return DelazifyCanonicalScriptedFunctionImpl
<Utf8Unit
>(cx
, fc
, scopeCache
,
1621 MOZ_ASSERT(ss
->hasSourceType
<char16_t
>());
1623 // UTF-16 source text.
1624 return DelazifyCanonicalScriptedFunctionImpl
<char16_t
>(cx
, fc
, scopeCache
,
1628 template <typename Unit
>
1629 static already_AddRefed
<CompilationStencil
>
1630 DelazifyCanonicalScriptedFunctionImpl(
1631 FrontendContext
* fc
, js::LifoAlloc
& tempLifoAlloc
,
1632 const JS::PrefableCompileOptions
& prefableOptions
,
1633 ScopeBindingCache
* scopeCache
, CompilationStencil
& context
,
1634 ScriptIndex scriptIndex
, DelazifyFailureReason
* failureReason
) {
1635 ScriptStencilRef script
{context
, scriptIndex
};
1636 const ScriptStencilExtra
& extra
= script
.scriptExtra();
1638 #if defined(EARLY_BETA_OR_EARLIER) || defined(DEBUG)
1639 const ScriptStencil
& data
= script
.scriptData();
1640 MOZ_ASSERT(!data
.hasSharedData(), "Script is already compiled!");
1641 MOZ_DIAGNOSTIC_ASSERT(!data
.isGhost());
1644 size_t sourceStart
= extra
.extent
.sourceStart
;
1645 size_t sourceLength
= extra
.extent
.sourceEnd
- sourceStart
;
1647 ScriptSource
* ss
= context
.source
;
1648 MOZ_ASSERT(ss
->hasSourceText());
1650 MOZ_ASSERT(ss
->hasSourceType
<Unit
>());
1652 ScriptSource::PinnedUnitsIfUncompressed
<Unit
> units(ss
, sourceStart
,
1655 *failureReason
= DelazifyFailureReason::Compressed
;
1659 JS::CompileOptions
options(prefableOptions
);
1660 options
.setMutedErrors(ss
->mutedErrors())
1661 .setFileAndLine(ss
->filename(), extra
.extent
.lineno
)
1662 .setColumn(JS::ColumnNumberOneOrigin(extra
.extent
.column
))
1663 .setScriptSourceOffset(sourceStart
)
1664 .setNoScriptRval(false)
1665 .setSelfHostingMode(false);
1667 // CompilationInput initialized with initFromStencil only reference
1668 // information from the CompilationStencil context and the ref-counted
1669 // ScriptSource, which are both GC-free.
1670 JS_HAZ_NON_GC_POINTER CompilationInput
input(options
);
1671 input
.initFromStencil(context
, scriptIndex
, ss
);
1673 using OutputType
= RefPtr
<CompilationStencil
>;
1674 BytecodeCompilerOutput
output((OutputType()));
1675 if (!CompileLazyFunctionToStencilMaybeInstantiate(
1676 nullptr, fc
, tempLifoAlloc
, input
, scopeCache
, units
.get(),
1677 sourceLength
, output
)) {
1678 *failureReason
= DelazifyFailureReason::Other
;
1681 return output
.as
<OutputType
>().forget();
1684 already_AddRefed
<CompilationStencil
>
1685 frontend::DelazifyCanonicalScriptedFunction(
1686 FrontendContext
* fc
, js::LifoAlloc
& tempLifoAlloc
,
1687 const JS::PrefableCompileOptions
& prefableOptions
,
1688 ScopeBindingCache
* scopeCache
, CompilationStencil
& context
,
1689 ScriptIndex scriptIndex
, DelazifyFailureReason
* failureReason
) {
1690 ScriptSource
* ss
= context
.source
;
1691 if (ss
->hasSourceType
<Utf8Unit
>()) {
1692 // UTF-8 source text.
1693 return DelazifyCanonicalScriptedFunctionImpl
<Utf8Unit
>(
1694 fc
, tempLifoAlloc
, prefableOptions
, scopeCache
, context
, scriptIndex
,
1698 // UTF-16 source text.
1699 MOZ_ASSERT(ss
->hasSourceType
<char16_t
>());
1700 return DelazifyCanonicalScriptedFunctionImpl
<char16_t
>(
1701 fc
, tempLifoAlloc
, prefableOptions
, scopeCache
, context
, scriptIndex
,
1705 static JSFunction
* CompileStandaloneFunction(
1706 JSContext
* cx
, const JS::ReadOnlyCompileOptions
& options
,
1707 JS::SourceText
<char16_t
>& srcBuf
, const Maybe
<uint32_t>& parameterListEnd
,
1708 FunctionSyntaxKind syntaxKind
, GeneratorKind generatorKind
,
1709 FunctionAsyncKind asyncKind
, JS::Handle
<Scope
*> enclosingScope
= nullptr) {
1710 JS::Rooted
<JSFunction
*> fun(cx
);
1712 AutoReportFrontendContext
fc(cx
);
1713 AutoAssertReportedException
assertException(cx
, &fc
);
1715 Rooted
<CompilationInput
> input(cx
, CompilationInput(options
));
1716 if (enclosingScope
) {
1717 if (!input
.get().initForStandaloneFunctionInNonSyntacticScope(
1718 &fc
, enclosingScope
)) {
1722 if (!input
.get().initForStandaloneFunction(cx
, &fc
)) {
1727 LifoAllocScope
parserAllocScope(&cx
->tempLifoAlloc());
1728 InheritThis inheritThis
= (syntaxKind
== FunctionSyntaxKind::Arrow
)
1731 ScopeBindingCache
* scopeCache
= &cx
->caches().scopeCache
;
1732 StandaloneFunctionCompiler
<char16_t
> compiler(&fc
, parserAllocScope
,
1733 input
.get(), srcBuf
);
1734 if (!compiler
.init(&fc
, scopeCache
, inheritThis
)) {
1738 if (!compiler
.compile(cx
, syntaxKind
, generatorKind
, asyncKind
,
1739 parameterListEnd
)) {
1743 Rooted
<CompilationGCOutput
> gcOutput(cx
);
1744 RefPtr
<ScriptSource
> source
;
1746 BorrowingCompilationStencil
borrowingStencil(compiler
.stencil());
1747 if (!CompilationStencil::instantiateStencils(
1748 cx
, input
.get(), borrowingStencil
, gcOutput
.get())) {
1751 source
= borrowingStencil
.source
;
1754 fun
= gcOutput
.get().getFunctionNoBaseIndex(
1755 CompilationStencil::TopLevelIndex
);
1756 MOZ_ASSERT(fun
->hasBytecode() || IsAsmJSModule(fun
));
1758 // Enqueue an off-thread source compression task after finishing parsing.
1759 if (!source
->tryCompressOffThread(cx
)) {
1763 // Note: If AsmJS successfully compiles, the into.script will still be
1764 // nullptr. In this case we have compiled to a native function instead of an
1765 // interpreted script.
1766 if (gcOutput
.get().script
) {
1767 if (parameterListEnd
) {
1768 source
->setParameterListEnd(*parameterListEnd
);
1771 const JS::InstantiateOptions
instantiateOptions(options
);
1772 Rooted
<JSScript
*> script(cx
, gcOutput
.get().script
);
1773 FireOnNewScript(cx
, instantiateOptions
, script
);
1776 assertException
.reset();
1781 JSFunction
* frontend::CompileStandaloneFunction(
1782 JSContext
* cx
, const JS::ReadOnlyCompileOptions
& options
,
1783 JS::SourceText
<char16_t
>& srcBuf
, const Maybe
<uint32_t>& parameterListEnd
,
1784 FunctionSyntaxKind syntaxKind
) {
1785 return CompileStandaloneFunction(cx
, options
, srcBuf
, parameterListEnd
,
1786 syntaxKind
, GeneratorKind::NotGenerator
,
1787 FunctionAsyncKind::SyncFunction
);
1790 JSFunction
* frontend::CompileStandaloneGenerator(
1791 JSContext
* cx
, const JS::ReadOnlyCompileOptions
& options
,
1792 JS::SourceText
<char16_t
>& srcBuf
, const Maybe
<uint32_t>& parameterListEnd
,
1793 FunctionSyntaxKind syntaxKind
) {
1794 return CompileStandaloneFunction(cx
, options
, srcBuf
, parameterListEnd
,
1795 syntaxKind
, GeneratorKind::Generator
,
1796 FunctionAsyncKind::SyncFunction
);
1799 JSFunction
* frontend::CompileStandaloneAsyncFunction(
1800 JSContext
* cx
, const ReadOnlyCompileOptions
& options
,
1801 JS::SourceText
<char16_t
>& srcBuf
, const Maybe
<uint32_t>& parameterListEnd
,
1802 FunctionSyntaxKind syntaxKind
) {
1803 return CompileStandaloneFunction(cx
, options
, srcBuf
, parameterListEnd
,
1804 syntaxKind
, GeneratorKind::NotGenerator
,
1805 FunctionAsyncKind::AsyncFunction
);
1808 JSFunction
* frontend::CompileStandaloneAsyncGenerator(
1809 JSContext
* cx
, const ReadOnlyCompileOptions
& options
,
1810 JS::SourceText
<char16_t
>& srcBuf
, const Maybe
<uint32_t>& parameterListEnd
,
1811 FunctionSyntaxKind syntaxKind
) {
1812 return CompileStandaloneFunction(cx
, options
, srcBuf
, parameterListEnd
,
1813 syntaxKind
, GeneratorKind::Generator
,
1814 FunctionAsyncKind::AsyncFunction
);
1817 JSFunction
* frontend::CompileStandaloneFunctionInNonSyntacticScope(
1818 JSContext
* cx
, const JS::ReadOnlyCompileOptions
& options
,
1819 JS::SourceText
<char16_t
>& srcBuf
, const Maybe
<uint32_t>& parameterListEnd
,
1820 FunctionSyntaxKind syntaxKind
, JS::Handle
<Scope
*> enclosingScope
) {
1821 MOZ_ASSERT(enclosingScope
);
1822 return CompileStandaloneFunction(cx
, options
, srcBuf
, parameterListEnd
,
1823 syntaxKind
, GeneratorKind::NotGenerator
,
1824 FunctionAsyncKind::SyncFunction
,