Source code

Revision control

Other Tools

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/. */
6
7
#include "frontend/BytecodeCompiler.h"
8
9
#include "mozilla/Attributes.h"
10
#include "mozilla/IntegerPrintfMacros.h"
11
#include "mozilla/Maybe.h"
12
#include "mozilla/Utf8.h" // mozilla::Utf8Unit
13
14
#include "builtin/ModuleObject.h"
15
#if defined(JS_BUILD_BINAST)
16
# include "frontend/BinASTParser.h"
17
#endif // JS_BUILD_BINAST
18
#include "frontend/BytecodeCompilation.h"
19
#include "frontend/BytecodeEmitter.h"
20
#include "frontend/EitherParser.h"
21
#include "frontend/ErrorReporter.h"
22
#include "frontend/FoldConstants.h"
23
#include "frontend/ModuleSharedContext.h"
24
#include "frontend/Parser.h"
25
#include "js/SourceText.h"
26
#include "vm/GlobalObject.h"
27
#include "vm/JSContext.h"
28
#include "vm/JSScript.h"
29
#include "vm/ModuleBuilder.h" // js::ModuleBuilder
30
#include "vm/TraceLogging.h"
31
#include "wasm/AsmJS.h"
32
33
#include "vm/EnvironmentObject-inl.h"
34
#include "vm/GeckoProfiler-inl.h"
35
#include "vm/JSContext-inl.h"
36
37
using namespace js;
38
using namespace js::frontend;
39
40
using mozilla::Maybe;
41
using mozilla::Nothing;
42
using mozilla::Utf8Unit;
43
44
using JS::CompileOptions;
45
using JS::ReadOnlyCompileOptions;
46
using JS::SourceText;
47
48
// CompileScript independently returns the ScriptSourceObject (SSO) for the
49
// compile. This is used by off-thread script compilation (OT-SC).
50
//
51
// OT-SC cannot initialize the SSO when it is first constructed because the
52
// SSO is allocated initially in a separate compartment.
53
//
54
// After OT-SC, the separate compartment is merged with the main compartment,
55
// at which point the JSScripts created become observable by the debugger via
56
// memory-space scanning.
57
//
58
// Whatever happens to the top-level script compilation (even if it fails and
59
// returns null), we must finish initializing the SSO. This is because there
60
// may be valid inner scripts observable by the debugger which reference the
61
// partially-initialized SSO.
62
class MOZ_STACK_CLASS AutoInitializeSourceObject {
63
BytecodeCompiler& compiler_;
64
ScriptSourceObject** sourceObjectOut_;
65
66
public:
67
AutoInitializeSourceObject(BytecodeCompiler& compiler,
68
ScriptSourceObject** sourceObjectOut)
69
: compiler_(compiler), sourceObjectOut_(sourceObjectOut) {}
70
71
inline ~AutoInitializeSourceObject() {
72
if (sourceObjectOut_) {
73
*sourceObjectOut_ = compiler_.sourceObjectPtr();
74
}
75
}
76
};
77
78
// RAII class to check the frontend reports an exception when it fails to
79
// compile a script.
80
class MOZ_RAII AutoAssertReportedException {
81
#ifdef DEBUG
82
JSContext* cx_;
83
bool check_;
84
85
public:
86
explicit AutoAssertReportedException(JSContext* cx) : cx_(cx), check_(true) {}
87
void reset() { check_ = false; }
88
~AutoAssertReportedException() {
89
if (!check_) {
90
return;
91
}
92
93
if (!cx_->isHelperThreadContext()) {
94
MOZ_ASSERT(cx_->isExceptionPending());
95
return;
96
}
97
98
ParseTask* task = cx_->parseTask();
99
MOZ_ASSERT(task->outOfMemory || task->overRecursed ||
100
!task->errors.empty());
101
}
102
#else
103
public:
104
explicit AutoAssertReportedException(JSContext*) {}
105
void reset() {}
106
#endif
107
};
108
109
template <typename Unit>
110
class MOZ_STACK_CLASS frontend::SourceAwareCompiler {
111
protected:
112
SourceText<Unit>& sourceBuffer_;
113
114
Maybe<Parser<SyntaxParseHandler, Unit>> syntaxParser;
115
Maybe<Parser<FullParseHandler, Unit>> parser;
116
117
using TokenStreamPosition = frontend::TokenStreamPosition<Unit>;
118
119
protected:
120
explicit SourceAwareCompiler(SourceText<Unit>& sourceBuffer)
121
: sourceBuffer_(sourceBuffer) {
122
MOZ_ASSERT(sourceBuffer_.get() != nullptr);
123
}
124
125
// Call this before calling compile{Global,Eval}Script.
126
MOZ_MUST_USE bool prepareScriptParse(BytecodeCompiler& info) {
127
return createSourceAndParser(info, ParseGoal::Script) &&
128
createCompleteScript(info);
129
}
130
131
void assertSourceAndParserCreated(BytecodeCompiler& info) const {
132
info.assertSourceCreated();
133
MOZ_ASSERT(info.usedNames.isSome());
134
MOZ_ASSERT(parser.isSome());
135
}
136
137
void assertSourceParserAndScriptCreated(BytecodeCompiler& info) {
138
assertSourceAndParserCreated(info);
139
MOZ_ASSERT(info.script != nullptr);
140
}
141
142
MOZ_MUST_USE bool emplaceEmitter(BytecodeCompiler& info,
143
Maybe<BytecodeEmitter>& emitter,
144
SharedContext* sharedContext) {
145
return info.emplaceEmitter(emitter, EitherParser(parser.ptr()),
146
sharedContext);
147
}
148
149
MOZ_MUST_USE bool createSourceAndParser(
150
BytecodeCompiler& compiler, ParseGoal goal,
151
const Maybe<uint32_t>& parameterListEnd = Nothing());
152
153
// This assumes the created script's offsets in the source used to parse it
154
// are the same as are used to compute its Function.prototype.toString()
155
// value.
156
MOZ_MUST_USE bool createCompleteScript(BytecodeCompiler& info) {
157
uint32_t toStringStart = 0;
158
uint32_t len = sourceBuffer_.length();
159
uint32_t toStringEnd = len;
160
return info.internalCreateScript(toStringStart, toStringEnd, len);
161
}
162
163
MOZ_MUST_USE bool handleParseFailure(BytecodeCompiler& compiler,
164
const Directives& newDirectives,
165
TokenStreamPosition& startPosition);
166
};
167
168
template <typename Unit>
169
class MOZ_STACK_CLASS frontend::ScriptCompiler
170
: public SourceAwareCompiler<Unit> {
171
using Base = SourceAwareCompiler<Unit>;
172
173
protected:
174
using Base::parser;
175
using Base::sourceBuffer_;
176
177
using Base::assertSourceParserAndScriptCreated;
178
using Base::emplaceEmitter;
179
using Base::handleParseFailure;
180
181
using typename Base::TokenStreamPosition;
182
183
public:
184
explicit ScriptCompiler(SourceText<Unit>& srcBuf) : Base(srcBuf) {}
185
186
MOZ_MUST_USE bool prepareScriptParse(BytecodeCompiler& compiler) {
187
return Base::prepareScriptParse(compiler);
188
}
189
190
JSScript* compileScript(BytecodeCompiler& compiler, HandleObject environment,
191
SharedContext* sc);
192
};
193
194
template <typename Unit>
195
static JSScript* CreateGlobalScript(
196
GlobalScriptInfo& info, JS::SourceText<Unit>& srcBuf,
197
ScriptSourceObject** sourceObjectOut = nullptr) {
198
AutoAssertReportedException assertException(info.context());
199
200
frontend::ScriptCompiler<Unit> compiler(srcBuf);
201
AutoInitializeSourceObject autoSSO(info, sourceObjectOut);
202
203
if (!compiler.prepareScriptParse(info)) {
204
return nullptr;
205
}
206
207
JSScript* script =
208
compiler.compileScript(info, nullptr, info.sharedContext());
209
if (!script) {
210
return nullptr;
211
}
212
213
assertException.reset();
214
return script;
215
}
216
217
JSScript* frontend::CompileGlobalScript(
218
GlobalScriptInfo& info, JS::SourceText<char16_t>& srcBuf,
219
ScriptSourceObject** sourceObjectOut /* = nullptr */) {
220
return CreateGlobalScript(info, srcBuf, sourceObjectOut);
221
}
222
223
JSScript* frontend::CompileGlobalScript(
224
GlobalScriptInfo& info, JS::SourceText<Utf8Unit>& srcBuf,
225
ScriptSourceObject** sourceObjectOut /* = nullptr */) {
226
return CreateGlobalScript(info, srcBuf, sourceObjectOut);
227
}
228
229
template <typename Unit>
230
static JSScript* CreateEvalScript(frontend::EvalScriptInfo& info,
231
SourceText<Unit>& srcBuf) {
232
AutoAssertReportedException assertException(info.context());
233
234
frontend::ScriptCompiler<Unit> compiler(srcBuf);
235
if (!compiler.prepareScriptParse(info)) {
236
return nullptr;
237
}
238
239
JSScript* script =
240
compiler.compileScript(info, info.environment(), info.sharedContext());
241
if (!script) {
242
return nullptr;
243
}
244
245
assertException.reset();
246
return script;
247
}
248
249
JSScript* frontend::CompileEvalScript(EvalScriptInfo& info,
250
JS::SourceText<char16_t>& srcBuf) {
251
return CreateEvalScript(info, srcBuf);
252
}
253
254
template <typename Unit>
255
class MOZ_STACK_CLASS frontend::ModuleCompiler final
256
: public SourceAwareCompiler<Unit> {
257
using Base = SourceAwareCompiler<Unit>;
258
259
using Base::assertSourceParserAndScriptCreated;
260
using Base::createCompleteScript;
261
using Base::createSourceAndParser;
262
using Base::emplaceEmitter;
263
using Base::parser;
264
265
public:
266
explicit ModuleCompiler(SourceText<Unit>& srcBuf) : Base(srcBuf) {}
267
268
ModuleObject* compile(ModuleInfo& info);
269
};
270
271
template <typename Unit>
272
class MOZ_STACK_CLASS frontend::StandaloneFunctionCompiler final
273
: public SourceAwareCompiler<Unit> {
274
using Base = SourceAwareCompiler<Unit>;
275
276
using Base::assertSourceAndParserCreated;
277
using Base::createSourceAndParser;
278
using Base::emplaceEmitter;
279
using Base::handleParseFailure;
280
using Base::parser;
281
using Base::sourceBuffer_;
282
283
using typename Base::TokenStreamPosition;
284
285
public:
286
explicit StandaloneFunctionCompiler(SourceText<Unit>& srcBuf)
287
: Base(srcBuf) {}
288
289
MOZ_MUST_USE bool prepare(StandaloneFunctionInfo& info,
290
const Maybe<uint32_t>& parameterListEnd) {
291
return createSourceAndParser(info, ParseGoal::Script, parameterListEnd);
292
}
293
294
FunctionNode* parse(StandaloneFunctionInfo& info, HandleFunction fun,
295
HandleScope enclosingScope, GeneratorKind generatorKind,
296
FunctionAsyncKind asyncKind,
297
const Maybe<uint32_t>& parameterListEnd);
298
299
MOZ_MUST_USE bool compile(MutableHandleFunction fun,
300
StandaloneFunctionInfo& info,
301
FunctionNode* parsedFunction);
302
303
private:
304
// Create a script for a function with the given toString offsets in source
305
// text.
306
MOZ_MUST_USE bool createFunctionScript(StandaloneFunctionInfo& info,
307
uint32_t toStringStart,
308
uint32_t toStringEnd) {
309
return info.internalCreateScript(toStringStart, toStringEnd,
310
sourceBuffer_.length());
311
}
312
};
313
314
AutoFrontendTraceLog::AutoFrontendTraceLog(JSContext* cx,
315
const TraceLoggerTextId id,
316
const ErrorReporter& errorReporter)
317
#ifdef JS_TRACE_LOGGING
318
: logger_(TraceLoggerForCurrentThread(cx)) {
319
if (!logger_) {
320
return;
321
}
322
323
// If the tokenizer hasn't yet gotten any tokens, use the line and column
324
// numbers from CompileOptions.
325
uint32_t line, column;
326
if (errorReporter.hasTokenizationStarted()) {
327
line = errorReporter.options().lineno;
328
column = errorReporter.options().column;
329
} else {
330
errorReporter.currentLineAndColumn(&line, &column);
331
}
332
frontendEvent_.emplace(TraceLogger_Frontend, errorReporter.getFilename(),
333
line, column);
334
frontendLog_.emplace(logger_, *frontendEvent_);
335
typeLog_.emplace(logger_, id);
336
}
337
#else
338
{
339
}
340
#endif
341
342
AutoFrontendTraceLog::AutoFrontendTraceLog(JSContext* cx,
343
const TraceLoggerTextId id,
344
const ErrorReporter& errorReporter,
345
FunctionBox* funbox)
346
#ifdef JS_TRACE_LOGGING
347
: logger_(TraceLoggerForCurrentThread(cx)) {
348
if (!logger_) {
349
return;
350
}
351
352
frontendEvent_.emplace(TraceLogger_Frontend, errorReporter.getFilename(),
353
funbox->startLine, funbox->startColumn);
354
frontendLog_.emplace(logger_, *frontendEvent_);
355
typeLog_.emplace(logger_, id);
356
}
357
#else
358
{
359
}
360
#endif
361
362
AutoFrontendTraceLog::AutoFrontendTraceLog(JSContext* cx,
363
const TraceLoggerTextId id,
364
const ErrorReporter& errorReporter,
365
ParseNode* pn)
366
#ifdef JS_TRACE_LOGGING
367
: logger_(TraceLoggerForCurrentThread(cx)) {
368
if (!logger_) {
369
return;
370
}
371
372
uint32_t line, column;
373
errorReporter.lineAndColumnAt(pn->pn_pos.begin, &line, &column);
374
frontendEvent_.emplace(TraceLogger_Frontend, errorReporter.getFilename(),
375
line, column);
376
frontendLog_.emplace(logger_, *frontendEvent_);
377
typeLog_.emplace(logger_, id);
378
}
379
#else
380
{
381
}
382
#endif
383
384
BytecodeCompiler::BytecodeCompiler(JSContext* cx,
385
const ReadOnlyCompileOptions& options)
386
: keepAtoms(cx),
387
cx(cx),
388
options(options),
389
sourceObject(cx),
390
directives(options.strictOption),
391
script(cx) {}
392
393
bool BytecodeCompiler::createScriptSource(
394
const Maybe<uint32_t>& parameterListEnd) {
395
sourceObject = CreateScriptSourceObject(cx, options, parameterListEnd);
396
if (!sourceObject) {
397
return false;
398
}
399
400
scriptSource = sourceObject->source();
401
return true;
402
}
403
404
bool BytecodeCompiler::canLazilyParse() const {
405
return options.canLazilyParse && !options.discardSource &&
406
!options.sourceIsLazy && !options.forceFullParse();
407
}
408
409
template <typename Unit>
410
bool frontend::SourceAwareCompiler<Unit>::createSourceAndParser(
411
BytecodeCompiler& info, ParseGoal goal,
412
const Maybe<uint32_t>& parameterListEnd /* = Nothing() */) {
413
if (!info.createScriptSource(parameterListEnd)) {
414
return false;
415
}
416
417
if (!info.assignSource(sourceBuffer_)) {
418
return false;
419
}
420
421
// Note the contents of any compiled scripts when recording/replaying.
422
if (mozilla::recordreplay::IsRecordingOrReplaying()) {
423
mozilla::recordreplay::NoteContentParse(
424
this, info.options.filename(), "application/javascript",
425
sourceBuffer_.units(), sourceBuffer_.length());
426
}
427
428
info.createUsedNames();
429
430
if (info.canLazilyParse()) {
431
syntaxParser.emplace(info.cx, info.cx->tempLifoAlloc(), info.options,
432
sourceBuffer_.units(), sourceBuffer_.length(),
433
/* foldConstants = */ false, *info.usedNames, nullptr,
434
nullptr, info.sourceObject, goal);
435
if (!syntaxParser->checkOptions()) {
436
return false;
437
}
438
}
439
440
parser.emplace(info.cx, info.cx->tempLifoAlloc(), info.options,
441
sourceBuffer_.units(), sourceBuffer_.length(),
442
/* foldConstants = */ true, *info.usedNames,
443
syntaxParser.ptrOr(nullptr), nullptr, info.sourceObject, goal);
444
parser->ss = info.scriptSource;
445
return parser->checkOptions();
446
}
447
448
bool BytecodeCompiler::internalCreateScript(uint32_t toStringStart,
449
uint32_t toStringEnd,
450
uint32_t sourceBufferLength) {
451
script = JSScript::Create(cx, options, sourceObject, /* sourceStart = */ 0,
452
sourceBufferLength, toStringStart, toStringEnd);
453
return script != nullptr;
454
}
455
456
bool BytecodeCompiler::emplaceEmitter(Maybe<BytecodeEmitter>& emitter,
457
const EitherParser& parser,
458
SharedContext* sharedContext) {
459
BytecodeEmitter::EmitterMode emitterMode = options.selfHostingMode
460
? BytecodeEmitter::SelfHosting
461
: BytecodeEmitter::Normal;
462
emitter.emplace(/* parent = */ nullptr, parser, sharedContext, script,
463
/* lazyScript = */ nullptr, options.lineno, options.column,
464
emitterMode);
465
return emitter->init();
466
}
467
468
template <typename Unit>
469
bool frontend::SourceAwareCompiler<Unit>::handleParseFailure(
470
BytecodeCompiler& info, const Directives& newDirectives,
471
TokenStreamPosition& startPosition) {
472
if (parser->hadAbortedSyntaxParse()) {
473
// Hit some unrecoverable ambiguity during an inner syntax parse.
474
// Syntax parsing has now been disabled in the parser, so retry
475
// the parse.
476
parser->clearAbortedSyntaxParse();
477
} else if (parser->anyChars.hadError() || info.directives == newDirectives) {
478
return false;
479
}
480
481
parser->tokenStream.seek(startPosition);
482
483
// Assignment must be monotonic to prevent reparsing iloops
484
MOZ_ASSERT_IF(info.directives.strict(), newDirectives.strict());
485
MOZ_ASSERT_IF(info.directives.asmJS(), newDirectives.asmJS());
486
info.directives = newDirectives;
487
return true;
488
}
489
490
template <typename Unit>
491
JSScript* frontend::ScriptCompiler<Unit>::compileScript(
492
BytecodeCompiler& info, HandleObject environment, SharedContext* sc) {
493
assertSourceParserAndScriptCreated(info);
494
495
TokenStreamPosition startPosition(info.keepAtoms, parser->tokenStream);
496
497
JSContext* cx = info.cx;
498
499
for (;;) {
500
ParseNode* pn;
501
{
502
AutoGeckoProfilerEntry pseudoFrame(cx, "script parsing",
503
JS::ProfilingCategoryPair::JS_Parsing);
504
if (sc->isEvalContext()) {
505
pn = parser->evalBody(sc->asEvalContext());
506
} else {
507
pn = parser->globalBody(sc->asGlobalContext());
508
}
509
}
510
511
// Successfully parsed. Emit the script.
512
AutoGeckoProfilerEntry pseudoFrame(cx, "script emit",
513
JS::ProfilingCategoryPair::JS_Parsing);
514
if (pn) {
515
// Publish deferred items
516
if (!parser->publishDeferredItems()) {
517
return nullptr;
518
}
519
520
Maybe<BytecodeEmitter> emitter;
521
if (!emplaceEmitter(info, emitter, sc)) {
522
return nullptr;
523
}
524
525
if (!emitter->emitScript(pn)) {
526
return nullptr;
527
}
528
529
// Success!
530
break;
531
}
532
533
// Maybe we aborted a syntax parse. See if we can try again.
534
if (!handleParseFailure(info, info.directives, startPosition)) {
535
return nullptr;
536
}
537
538
// Reset preserved state before trying again.
539
info.usedNames->reset();
540
parser->getTreeHolder().resetFunctionTree();
541
}
542
543
// We have just finished parsing the source. Inform the source so that we
544
// can compute statistics (e.g. how much time our functions remain lazy).
545
info.script->scriptSource()->recordParseEnded();
546
547
// Enqueue an off-thread source compression task after finishing parsing.
548
if (!info.scriptSource->tryCompressOffThread(cx)) {
549
return nullptr;
550
}
551
552
MOZ_ASSERT_IF(!cx->isHelperThreadContext(), !cx->isExceptionPending());
553
554
return info.script;
555
}
556
557
template <typename Unit>
558
ModuleObject* frontend::ModuleCompiler<Unit>::compile(ModuleInfo& info) {
559
if (!createSourceAndParser(info, ParseGoal::Module) ||
560
!createCompleteScript(info)) {
561
return nullptr;
562
}
563
564
JSContext* cx = info.cx;
565
566
Rooted<ModuleObject*> module(cx, ModuleObject::create(cx));
567
if (!module) {
568
return nullptr;
569
}
570
571
module->init(info.script);
572
573
ModuleBuilder builder(cx, module, parser.ptr());
574
575
RootedScope enclosingScope(cx, &cx->global()->emptyGlobalScope());
576
ModuleSharedContext modulesc(cx, module, enclosingScope, builder);
577
ParseNode* pn = parser->moduleBody(&modulesc);
578
if (!pn) {
579
return nullptr;
580
}
581
582
if (!parser->publishDeferredItems()) {
583
return nullptr;
584
}
585
586
Maybe<BytecodeEmitter> emitter;
587
if (!emplaceEmitter(info, emitter, &modulesc)) {
588
return nullptr;
589
}
590
591
if (!emitter->emitScript(pn->as<ModuleNode>().body())) {
592
return nullptr;
593
}
594
595
if (!builder.initModule()) {
596
return nullptr;
597
}
598
599
RootedModuleEnvironmentObject env(
600
cx, ModuleEnvironmentObject::create(cx, module));
601
if (!env) {
602
return nullptr;
603
}
604
605
module->setInitialEnvironment(env);
606
607
// Enqueue an off-thread source compression task after finishing parsing.
608
if (!info.scriptSource->tryCompressOffThread(cx)) {
609
return nullptr;
610
}
611
612
MOZ_ASSERT_IF(!cx->isHelperThreadContext(), !cx->isExceptionPending());
613
return module;
614
}
615
616
// Parse a standalone JS function, which might appear as the value of an
617
// event handler attribute in an HTML <INPUT> tag, or in a Function()
618
// constructor.
619
template <typename Unit>
620
FunctionNode* frontend::StandaloneFunctionCompiler<Unit>::parse(
621
StandaloneFunctionInfo& info, HandleFunction fun,
622
HandleScope enclosingScope, GeneratorKind generatorKind,
623
FunctionAsyncKind asyncKind, const Maybe<uint32_t>& parameterListEnd) {
624
MOZ_ASSERT(fun);
625
MOZ_ASSERT(fun->isTenured());
626
627
assertSourceAndParserCreated(info);
628
629
TokenStreamPosition startPosition(info.keepAtoms, parser->tokenStream);
630
631
// Speculatively parse using the default directives implied by the context.
632
// If a directive is encountered (e.g., "use strict") that changes how the
633
// function should have been parsed, we backup and reparse with the new set
634
// of directives.
635
636
FunctionNode* fn;
637
do {
638
Directives newDirectives = info.directives;
639
fn = parser->standaloneFunction(fun, enclosingScope, parameterListEnd,
640
generatorKind, asyncKind, info.directives,
641
&newDirectives);
642
if (!fn && !handleParseFailure(info, newDirectives, startPosition)) {
643
return nullptr;
644
}
645
} while (!fn);
646
647
return fn;
648
}
649
650
// Compile a standalone JS function.
651
template <typename Unit>
652
bool frontend::StandaloneFunctionCompiler<Unit>::compile(
653
MutableHandleFunction fun, StandaloneFunctionInfo& info,
654
FunctionNode* parsedFunction) {
655
FunctionBox* funbox = parsedFunction->funbox();
656
if (funbox->isInterpreted()) {
657
MOZ_ASSERT(fun == funbox->function());
658
659
if (!createFunctionScript(info, funbox->toStringStart,
660
funbox->toStringEnd)) {
661
return false;
662
}
663
664
if (!parser->publishDeferredItems()) {
665
return false;
666
}
667
668
Maybe<BytecodeEmitter> emitter;
669
if (!emplaceEmitter(info, emitter, funbox)) {
670
return false;
671
}
672
673
if (!emitter->emitFunctionScript(parsedFunction,
674
BytecodeEmitter::TopLevelFunction::Yes)) {
675
return false;
676
}
677
} else {
678
fun.set(funbox->function());
679
MOZ_ASSERT(IsAsmJSModule(fun));
680
}
681
682
// Enqueue an off-thread source compression task after finishing parsing.
683
return info.scriptSource->tryCompressOffThread(info.cx);
684
}
685
686
ScriptSourceObject* frontend::CreateScriptSourceObject(
687
JSContext* cx, const ReadOnlyCompileOptions& options,
688
const Maybe<uint32_t>& parameterListEnd /* = Nothing() */) {
689
ScriptSource* ss = cx->new_<ScriptSource>();
690
if (!ss) {
691
return nullptr;
692
}
693
ScriptSourceHolder ssHolder(ss);
694
695
if (!ss->initFromOptions(cx, options, parameterListEnd)) {
696
return nullptr;
697
}
698
699
RootedScriptSourceObject sso(cx, ScriptSourceObject::create(cx, ss));
700
if (!sso) {
701
return nullptr;
702
}
703
704
// Off-thread compilations do all their GC heap allocation, including the
705
// SSO, in a temporary compartment. Hence, for the SSO to refer to the
706
// gc-heap-allocated values in |options|, it would need cross-compartment
707
// wrappers from the temporary compartment to the real compartment --- which
708
// would then be inappropriate once we merged the temporary and real
709
// compartments.
710
//
711
// Instead, we put off populating those SSO slots in off-thread compilations
712
// until after we've merged compartments.
713
if (!cx->isHelperThreadContext()) {
714
if (!ScriptSourceObject::initFromOptions(cx, sso, options)) {
715
return nullptr;
716
}
717
}
718
719
return sso;
720
}
721
722
#if defined(JS_BUILD_BINAST)
723
724
JSScript* frontend::CompileGlobalBinASTScript(
725
JSContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options,
726
const uint8_t* src, size_t len, ScriptSourceObject** sourceObjectOut) {
727
AutoAssertReportedException assertException(cx);
728
729
frontend::UsedNameTracker usedNames(cx);
730
731
RootedScriptSourceObject sourceObj(cx, CreateScriptSourceObject(cx, options));
732
733
if (!sourceObj) {
734
return nullptr;
735
}
736
737
if (!sourceObj->source()->setBinASTSourceCopy(cx, src, len)) {
738
return nullptr;
739
}
740
741
RootedScript script(cx,
742
JSScript::Create(cx, options, sourceObj, 0, len, 0, len));
743
744
if (!script) {
745
return nullptr;
746
}
747
748
Directives directives(options.strictOption);
749
GlobalSharedContext globalsc(cx, ScopeKind::Global, directives,
750
options.extraWarningsOption);
751
752
frontend::BinASTParser<BinASTTokenReaderMultipart> parser(
753
cx, alloc, usedNames, options, sourceObj);
754
755
// Metadata stores internal pointers, so we must use the same buffer every
756
// time, including for lazy parses
757
ScriptSource* ss = sourceObj->source();
758
BinASTSourceMetadata* metadata = nullptr;
759
auto parsed =
760
parser.parse(&globalsc, ss->binASTSource(), ss->length(), &metadata);
761
762
if (parsed.isErr()) {
763
return nullptr;
764
}
765
766
sourceObj->source()->setBinASTSourceMetadata(metadata);
767
768
BytecodeEmitter bce(nullptr, &parser, &globalsc, script, nullptr, 0, 0);
769
770
if (!bce.init()) {
771
return nullptr;
772
}
773
774
ParseNode* pn = parsed.unwrap();
775
if (!bce.emitScript(pn)) {
776
return nullptr;
777
}
778
779
if (sourceObjectOut) {
780
*sourceObjectOut = sourceObj;
781
}
782
783
assertException.reset();
784
return script;
785
}
786
787
#endif // JS_BUILD_BINAST
788
789
template <typename Unit>
790
static ModuleObject* InternalParseModule(
791
JSContext* cx, const ReadOnlyCompileOptions& optionsInput,
792
SourceText<Unit>& srcBuf, ScriptSourceObject** sourceObjectOut) {
793
MOZ_ASSERT(srcBuf.get());
794
MOZ_ASSERT_IF(sourceObjectOut, *sourceObjectOut == nullptr);
795
796
AutoAssertReportedException assertException(cx);
797
798
CompileOptions options(cx, optionsInput);
799
options.maybeMakeStrictMode(
800
true); // ES6 10.2.1 Module code is always strict mode code.
801
options.setIsRunOnce(true);
802
options.allowHTMLComments = false;
803
804
ModuleInfo info(cx, options);
805
AutoInitializeSourceObject autoSSO(info, sourceObjectOut);
806
807
ModuleCompiler<Unit> compiler(srcBuf);
808
ModuleObject* module = compiler.compile(info);
809
if (!module) {
810
return nullptr;
811
}
812
813
assertException.reset();
814
return module;
815
}
816
817
ModuleObject* frontend::ParseModule(JSContext* cx,
818
const ReadOnlyCompileOptions& optionsInput,
819
SourceText<char16_t>& srcBuf,
820
ScriptSourceObject** sourceObjectOut) {
821
return InternalParseModule(cx, optionsInput, srcBuf, sourceObjectOut);
822
}
823
824
ModuleObject* frontend::ParseModule(JSContext* cx,
825
const ReadOnlyCompileOptions& optionsInput,
826
SourceText<Utf8Unit>& srcBuf,
827
ScriptSourceObject** sourceObjectOut) {
828
return InternalParseModule(cx, optionsInput, srcBuf, sourceObjectOut);
829
}
830
831
template <typename Unit>
832
static ModuleObject* CreateModule(JSContext* cx,
833
const JS::ReadOnlyCompileOptions& options,
834
SourceText<Unit>& srcBuf) {
835
AutoAssertReportedException assertException(cx);
836
837
if (!GlobalObject::ensureModulePrototypesCreated(cx, cx->global())) {
838
return nullptr;
839
}
840
841
RootedModuleObject module(cx, ParseModule(cx, options, srcBuf, nullptr));
842
if (!module) {
843
return nullptr;
844
}
845
846
// This happens in GlobalHelperThreadState::finishModuleParseTask() when a
847
// module is compiled off thread.
848
if (!ModuleObject::Freeze(cx, module)) {
849
return nullptr;
850
}
851
852
assertException.reset();
853
return module;
854
}
855
856
ModuleObject* frontend::CompileModule(JSContext* cx,
857
const JS::ReadOnlyCompileOptions& options,
858
SourceText<char16_t>& srcBuf) {
859
return CreateModule(cx, options, srcBuf);
860
}
861
862
ModuleObject* frontend::CompileModule(JSContext* cx,
863
const JS::ReadOnlyCompileOptions& options,
864
SourceText<Utf8Unit>& srcBuf) {
865
return CreateModule(cx, options, srcBuf);
866
}
867
868
// When leaving this scope, the given function should either:
869
// * be linked to a fully compiled script
870
// * remain linking to a lazy script
871
class MOZ_STACK_CLASS AutoAssertFunctionDelazificationCompletion {
872
#ifdef DEBUG
873
RootedFunction fun_;
874
#endif
875
876
public:
877
AutoAssertFunctionDelazificationCompletion(JSContext* cx, HandleFunction fun)
878
#ifdef DEBUG
879
: fun_(cx, fun)
880
#endif
881
{
882
MOZ_ASSERT(fun_->isInterpretedLazy());
883
MOZ_ASSERT(!fun_->lazyScript()->hasScript());
884
}
885
886
~AutoAssertFunctionDelazificationCompletion() {
887
#ifdef DEBUG
888
if (!fun_) {
889
return;
890
}
891
#endif
892
893
// If fun_ is not nullptr, it means delazification doesn't complete.
894
// Assert that the function keeps linking to lazy script
895
MOZ_ASSERT(fun_->isInterpretedLazy());
896
MOZ_ASSERT(!fun_->lazyScript()->hasScript());
897
}
898
899
void complete() {
900
// Assert the completion of delazification and forget the function.
901
MOZ_ASSERT(fun_->hasScript());
902
MOZ_ASSERT(!fun_->hasUncompletedScript());
903
904
#ifdef DEBUG
905
fun_ = nullptr;
906
#endif
907
}
908
};
909
910
template <typename Unit>
911
static bool CompileLazyFunctionImpl(JSContext* cx, Handle<LazyScript*> lazy,
912
const Unit* units, size_t length) {
913
MOZ_ASSERT(cx->compartment() ==
914
lazy->functionNonDelazifying()->compartment());
915
916
// We can only compile functions whose parents have previously been
917
// compiled, because compilation requires full information about the
918
// function's immediately enclosing scope.
919
MOZ_ASSERT(lazy->enclosingScriptHasEverBeenCompiled());
920
921
MOZ_ASSERT(!lazy->isBinAST());
922
923
AutoAssertReportedException assertException(cx);
924
Rooted<JSFunction*> fun(cx, lazy->functionNonDelazifying());
925
AutoAssertFunctionDelazificationCompletion delazificationCompletion(cx, fun);
926
927
JS::CompileOptions options(cx);
928
options.setMutedErrors(lazy->mutedErrors())
929
.setFileAndLine(lazy->filename(), lazy->lineno())
930
.setColumn(lazy->column())
931
.setScriptSourceOffset(lazy->sourceStart())
932
.setNoScriptRval(false)
933
.setSelfHostingMode(false);
934
935
// Update statistics to find out if we are delazifying just after having
936
// lazified. Note that we are interested in the delta between end of
937
// syntax parsing and start of full parsing, so we do this now rather than
938
// after parsing below.
939
if (!lazy->scriptSource()->parseEnded().IsNull()) {
940
const mozilla::TimeDuration delta =
941
ReallyNow() - lazy->scriptSource()->parseEnded();
942
943
// Differentiate between web-facing and privileged code, to aid
944
// with optimization. Due to the number of calls to this function,
945
// we use `cx->runningWithTrustedPrincipals`, which is fast but
946
// will classify addons alongside with web-facing code.
947
const int HISTOGRAM =
948
cx->runningWithTrustedPrincipals()
949
? JS_TELEMETRY_PRIVILEGED_PARSER_COMPILE_LAZY_AFTER_MS
950
: JS_TELEMETRY_WEB_PARSER_COMPILE_LAZY_AFTER_MS;
951
cx->runtime()->addTelemetry(HISTOGRAM, delta.ToMilliseconds());
952
}
953
954
UsedNameTracker usedNames(cx);
955
956
RootedScriptSourceObject sourceObject(cx, lazy->sourceObject());
957
Parser<FullParseHandler, Unit> parser(
958
cx, cx->tempLifoAlloc(), options, units, length,
959
/* foldConstants = */ true, usedNames, nullptr, lazy, sourceObject,
960
lazy->parseGoal());
961
if (!parser.checkOptions()) {
962
return false;
963
}
964
965
FunctionNode* pn =
966
parser.standaloneLazyFunction(fun, lazy->toStringStart(), lazy->strict(),
967
lazy->generatorKind(), lazy->asyncKind());
968
if (!pn) {
969
return false;
970
}
971
972
Rooted<JSScript*> script(cx, JSScript::CreateFromLazy(cx, lazy));
973
if (!script) {
974
return false;
975
}
976
977
if (lazy->isLikelyConstructorWrapper()) {
978
script->setLikelyConstructorWrapper();
979
}
980
if (lazy->hasBeenCloned()) {
981
script->setHasBeenCloned();
982
}
983
984
FieldInitializers fieldInitializers = FieldInitializers::Invalid();
985
if (fun->kind() == FunctionFlags::FunctionKind::ClassConstructor) {
986
fieldInitializers = lazy->getFieldInitializers();
987
}
988
989
BytecodeEmitter bce(/* parent = */ nullptr, &parser, pn->funbox(), script,
990
lazy, lazy->lineno(), lazy->column(),
991
BytecodeEmitter::LazyFunction, fieldInitializers);
992
if (!bce.init(pn->pn_pos)) {
993
return false;
994
}
995
996
if (!bce.emitFunctionScript(pn, BytecodeEmitter::TopLevelFunction::Yes)) {
997
return false;
998
}
999
1000
MOZ_ASSERT(lazy->hasDirectEval() == script->hasDirectEval());
1001
1002
delazificationCompletion.complete();
1003
assertException.reset();
1004
return true;
1005
}
1006
1007
bool frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy,
1008
const char16_t* units, size_t length) {
1009
return CompileLazyFunctionImpl(cx, lazy, units, length);
1010
}
1011
1012
bool frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy,
1013
const Utf8Unit* units, size_t length) {
1014
return CompileLazyFunctionImpl(cx, lazy, units, length);
1015
}
1016
1017
#ifdef JS_BUILD_BINAST
1018
1019
bool frontend::CompileLazyBinASTFunction(JSContext* cx,
1020
Handle<LazyScript*> lazy,
1021
const uint8_t* buf, size_t length) {
1022
MOZ_ASSERT(cx->compartment() ==
1023
lazy->functionNonDelazifying()->compartment());
1024
1025
// We can only compile functions whose parents have previously been
1026
// compiled, because compilation requires full information about the
1027
// function's immediately enclosing scope.
1028
MOZ_ASSERT(lazy->enclosingScriptHasEverBeenCompiled());
1029
MOZ_ASSERT(lazy->isBinAST());
1030
1031
AutoAssertReportedException assertException(cx);
1032
Rooted<JSFunction*> fun(cx, lazy->functionNonDelazifying());
1033
AutoAssertFunctionDelazificationCompletion delazificationCompletion(cx, fun);
1034
1035
CompileOptions options(cx);
1036
options.setMutedErrors(lazy->mutedErrors())
1037
.setFileAndLine(lazy->filename(), lazy->lineno())
1038
.setColumn(lazy->column())
1039
.setScriptSourceOffset(lazy->sourceStart())
1040
.setNoScriptRval(false)
1041
.setSelfHostingMode(false);
1042
1043
UsedNameTracker usedNames(cx);
1044
1045
RootedScriptSourceObject sourceObj(cx, lazy->sourceObject());
1046
MOZ_ASSERT(sourceObj);
1047
1048
RootedScript script(
1049
cx, JSScript::Create(cx, options, sourceObj, lazy->sourceStart(),
1050
lazy->sourceEnd(), lazy->sourceStart(),
1051
lazy->sourceEnd()));
1052
1053
if (!script) {
1054
return false;
1055
}
1056
1057
if (lazy->hasBeenCloned()) {
1058
script->setHasBeenCloned();
1059
}
1060
1061
frontend::BinASTParser<BinASTTokenReaderMultipart> parser(
1062
cx, cx->tempLifoAlloc(), usedNames, options, sourceObj, lazy);
1063
1064
auto parsed =
1065
parser.parseLazyFunction(lazy->scriptSource(), lazy->sourceStart());
1066
1067
if (parsed.isErr()) {
1068
return false;
1069
}
1070
1071
FunctionNode* pn = parsed.unwrap();
1072
1073
BytecodeEmitter bce(nullptr, &parser, pn->funbox(), script, lazy,
1074
lazy->lineno(), lazy->column(),
1075
BytecodeEmitter::LazyFunction);
1076
1077
if (!bce.init(pn->pn_pos)) {
1078
return false;
1079
}
1080
1081
if (!bce.emitFunctionScript(pn, BytecodeEmitter::TopLevelFunction::Yes)) {
1082
return false;
1083
}
1084
1085
delazificationCompletion.complete();
1086
assertException.reset();
1087
return script;
1088
}
1089
1090
#endif // JS_BUILD_BINAST
1091
1092
static bool CompileStandaloneFunction(JSContext* cx, MutableHandleFunction fun,
1093
const JS::ReadOnlyCompileOptions& options,
1094
JS::SourceText<char16_t>& srcBuf,
1095
const Maybe<uint32_t>& parameterListEnd,
1096
GeneratorKind generatorKind,
1097
FunctionAsyncKind asyncKind,
1098
HandleScope enclosingScope = nullptr) {
1099
AutoAssertReportedException assertException(cx);
1100
1101
StandaloneFunctionInfo info(cx, options);
1102
1103
StandaloneFunctionCompiler<char16_t> compiler(srcBuf);
1104
if (!compiler.prepare(info, parameterListEnd)) {
1105
return false;
1106
}
1107
1108
RootedScope scope(cx, enclosingScope);
1109
if (!scope) {
1110
scope = &cx->global()->emptyGlobalScope();
1111
}
1112
1113
FunctionNode* parsedFunction = compiler.parse(info, fun, scope, generatorKind,
1114
asyncKind, parameterListEnd);
1115
if (!parsedFunction || !compiler.compile(fun, info, parsedFunction)) {
1116
return false;
1117
}
1118
1119
assertException.reset();
1120
return true;
1121
}
1122
1123
bool frontend::CompileStandaloneFunction(
1124
JSContext* cx, MutableHandleFunction fun,
1125
const JS::ReadOnlyCompileOptions& options, JS::SourceText<char16_t>& srcBuf,
1126
const Maybe<uint32_t>& parameterListEnd,
1127
HandleScope enclosingScope /* = nullptr */) {
1128
return CompileStandaloneFunction(
1129
cx, fun, options, srcBuf, parameterListEnd, GeneratorKind::NotGenerator,
1130
FunctionAsyncKind::SyncFunction, enclosingScope);
1131
}
1132
1133
bool frontend::CompileStandaloneGenerator(
1134
JSContext* cx, MutableHandleFunction fun,
1135
const JS::ReadOnlyCompileOptions& options, JS::SourceText<char16_t>& srcBuf,
1136
const Maybe<uint32_t>& parameterListEnd) {
1137
return CompileStandaloneFunction(cx, fun, options, srcBuf, parameterListEnd,
1138
GeneratorKind::Generator,
1139
FunctionAsyncKind::SyncFunction);
1140
}
1141
1142
bool frontend::CompileStandaloneAsyncFunction(
1143
JSContext* cx, MutableHandleFunction fun,
1144
const ReadOnlyCompileOptions& options, JS::SourceText<char16_t>& srcBuf,
1145
const Maybe<uint32_t>& parameterListEnd) {
1146
return CompileStandaloneFunction(cx, fun, options, srcBuf, parameterListEnd,
1147
GeneratorKind::NotGenerator,
1148
FunctionAsyncKind::AsyncFunction);
1149
}
1150
1151
bool frontend::CompileStandaloneAsyncGenerator(
1152
JSContext* cx, MutableHandleFunction fun,
1153
const ReadOnlyCompileOptions& options, JS::SourceText<char16_t>& srcBuf,
1154
const Maybe<uint32_t>& parameterListEnd) {
1155
return CompileStandaloneFunction(cx, fun, options, srcBuf, parameterListEnd,
1156
GeneratorKind::Generator,
1157
FunctionAsyncKind::AsyncFunction);
1158
}