Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
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/IntegerPrintfMacros.h"
10 : #include "mozilla/Maybe.h"
11 :
12 : #include "jscntxt.h"
13 : #include "jsscript.h"
14 :
15 : #include "builtin/ModuleObject.h"
16 : #include "frontend/BytecodeEmitter.h"
17 : #include "frontend/FoldConstants.h"
18 : #include "frontend/NameFunctions.h"
19 : #include "frontend/Parser.h"
20 : #include "vm/GlobalObject.h"
21 : #include "vm/TraceLogging.h"
22 : #include "wasm/AsmJS.h"
23 :
24 : #include "jsobjinlines.h"
25 : #include "jsscriptinlines.h"
26 :
27 : #include "vm/EnvironmentObject-inl.h"
28 :
29 : using namespace js;
30 : using namespace js::frontend;
31 : using mozilla::Maybe;
32 : using mozilla::Nothing;
33 :
34 : // The BytecodeCompiler class contains resources common to compiling scripts and
35 : // function bodies.
36 284 : class MOZ_STACK_CLASS BytecodeCompiler
37 : {
38 : public:
39 : // Construct an object passing mandatory arguments.
40 : BytecodeCompiler(JSContext* cx,
41 : LifoAlloc& alloc,
42 : const ReadOnlyCompileOptions& options,
43 : SourceBufferHolder& sourceBuffer,
44 : HandleScope enclosingScope);
45 :
46 : JSScript* compileGlobalScript(ScopeKind scopeKind);
47 : JSScript* compileEvalScript(HandleObject environment, HandleScope enclosingScope);
48 : ModuleObject* compileModule();
49 : bool compileStandaloneFunction(MutableHandleFunction fun, GeneratorKind generatorKind,
50 : FunctionAsyncKind asyncKind,
51 : const Maybe<uint32_t>& parameterListEnd);
52 :
53 : ScriptSourceObject* sourceObjectPtr() const;
54 : SourceCompressionTask* sourceCompressionTask() const;
55 :
56 : private:
57 : JSScript* compileScript(HandleObject environment, SharedContext* sc);
58 : bool checkLength();
59 : bool createScriptSource(const Maybe<uint32_t>& parameterListEnd);
60 : bool canLazilyParse();
61 : bool createParser();
62 : bool createSourceAndParser(const Maybe<uint32_t>& parameterListEnd = Nothing());
63 :
64 : // If toString{Start,End} are not explicitly passed, assume the script's
65 : // offsets in the source used to parse it are the same as what should be
66 : // used to compute its Function.prototype.toString() value.
67 : bool createScript();
68 : bool createScript(uint32_t toStringStart, uint32_t toStringEnd);
69 :
70 : bool emplaceEmitter(Maybe<BytecodeEmitter>& emitter, SharedContext* sharedContext);
71 : bool handleParseFailure(const Directives& newDirectives);
72 : bool deoptimizeArgumentsInEnclosingScripts(JSContext* cx, HandleObject environment);
73 :
74 : AutoKeepAtoms keepAtoms;
75 :
76 : JSContext* cx;
77 : LifoAlloc& alloc;
78 : const ReadOnlyCompileOptions& options;
79 : SourceBufferHolder& sourceBuffer;
80 :
81 : RootedScope enclosingScope;
82 :
83 : RootedScriptSource sourceObject;
84 : ScriptSource* scriptSource;
85 :
86 : Maybe<UsedNameTracker> usedNames;
87 : Maybe<Parser<SyntaxParseHandler, char16_t>> syntaxParser;
88 : Maybe<Parser<FullParseHandler, char16_t>> parser;
89 :
90 : Directives directives;
91 : TokenStream::Position startPosition;
92 :
93 : RootedScript script;
94 : };
95 :
96 0 : AutoFrontendTraceLog::AutoFrontendTraceLog(JSContext* cx, const TraceLoggerTextId id,
97 0 : const char* filename, size_t line, size_t column)
98 : #ifdef JS_TRACE_LOGGING
99 0 : : logger_(TraceLoggerForCurrentThread(cx))
100 : {
101 0 : frontendEvent_.emplace(TraceLogger_Frontend, filename, line, column);
102 0 : frontendLog_.emplace(logger_, *frontendEvent_);
103 0 : typeLog_.emplace(logger_, id);
104 0 : }
105 : #else
106 : { }
107 : #endif
108 :
109 7646 : AutoFrontendTraceLog::AutoFrontendTraceLog(JSContext* cx, const TraceLoggerTextId id,
110 7646 : const TokenStreamAnyChars& tokenStream)
111 : #ifdef JS_TRACE_LOGGING
112 7646 : : logger_(TraceLoggerForCurrentThread(cx))
113 : {
114 : // If the tokenizer hasn't yet gotten any tokens, use the line and column
115 : // numbers from CompileOptions.
116 : uint32_t line, column;
117 7646 : if (tokenStream.isCurrentTokenType(TOK_EOF) && !tokenStream.isEOF()) {
118 1676 : line = tokenStream.options().lineno;
119 1676 : column = tokenStream.options().column;
120 : } else {
121 5970 : uint32_t offset = tokenStream.currentToken().pos.begin;
122 5970 : tokenStream.srcCoords.lineNumAndColumnIndex(offset, &line, &column);
123 : }
124 7646 : frontendEvent_.emplace(TraceLogger_Frontend, tokenStream.getFilename(), line, column);
125 7646 : frontendLog_.emplace(logger_, *frontendEvent_);
126 7646 : typeLog_.emplace(logger_, id);
127 7646 : }
128 : #else
129 : { }
130 : #endif
131 :
132 5404 : AutoFrontendTraceLog::AutoFrontendTraceLog(JSContext* cx, const TraceLoggerTextId id,
133 : const TokenStreamAnyChars& tokenStream,
134 5404 : FunctionBox* funbox)
135 : #ifdef JS_TRACE_LOGGING
136 5404 : : logger_(TraceLoggerForCurrentThread(cx))
137 : {
138 10808 : frontendEvent_.emplace(TraceLogger_Frontend, tokenStream.getFilename(),
139 5404 : funbox->startLine, funbox->startColumn);
140 5404 : frontendLog_.emplace(logger_, *frontendEvent_);
141 5404 : typeLog_.emplace(logger_, id);
142 5404 : }
143 : #else
144 : { }
145 : #endif
146 :
147 268 : AutoFrontendTraceLog::AutoFrontendTraceLog(JSContext* cx, const TraceLoggerTextId id,
148 268 : const TokenStreamAnyChars& tokenStream, ParseNode* pn)
149 : #ifdef JS_TRACE_LOGGING
150 268 : : logger_(TraceLoggerForCurrentThread(cx))
151 : {
152 : uint32_t line, column;
153 268 : tokenStream.srcCoords.lineNumAndColumnIndex(pn->pn_pos.begin, &line, &column);
154 268 : frontendEvent_.emplace(TraceLogger_Frontend, tokenStream.getFilename(), line, column);
155 268 : frontendLog_.emplace(logger_, *frontendEvent_);
156 268 : typeLog_.emplace(logger_, id);
157 268 : }
158 : #else
159 : { }
160 : #endif
161 :
162 284 : BytecodeCompiler::BytecodeCompiler(JSContext* cx,
163 : LifoAlloc& alloc,
164 : const ReadOnlyCompileOptions& options,
165 : SourceBufferHolder& sourceBuffer,
166 284 : HandleScope enclosingScope)
167 : : keepAtoms(cx),
168 : cx(cx),
169 : alloc(alloc),
170 : options(options),
171 : sourceBuffer(sourceBuffer),
172 : enclosingScope(cx, enclosingScope),
173 : sourceObject(cx),
174 : scriptSource(nullptr),
175 284 : directives(options.strictOption),
176 : startPosition(keepAtoms),
177 568 : script(cx)
178 : {
179 284 : MOZ_ASSERT(sourceBuffer.get());
180 284 : }
181 :
182 : bool
183 284 : BytecodeCompiler::checkLength()
184 : {
185 : // Note this limit is simply so we can store sourceStart and sourceEnd in
186 : // JSScript as 32-bits. It could be lifted fairly easily, since the compiler
187 : // is using size_t internally already.
188 284 : if (sourceBuffer.length() > UINT32_MAX) {
189 0 : if (!cx->helperThread())
190 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
191 0 : JSMSG_SOURCE_TOO_LONG);
192 0 : return false;
193 : }
194 284 : return true;
195 : }
196 :
197 : bool
198 284 : BytecodeCompiler::createScriptSource(const Maybe<uint32_t>& parameterListEnd)
199 : {
200 284 : if (!checkLength())
201 0 : return false;
202 :
203 284 : sourceObject = CreateScriptSourceObject(cx, options, parameterListEnd);
204 284 : if (!sourceObject)
205 0 : return false;
206 :
207 284 : scriptSource = sourceObject->source();
208 :
209 284 : if (!cx->compartment()->behaviors().discardSource()) {
210 281 : if (options.sourceIsLazy) {
211 64 : scriptSource->setSourceRetrievable();
212 217 : } else if (!scriptSource->setSourceCopy(cx, sourceBuffer)) {
213 0 : return false;
214 : }
215 : }
216 :
217 284 : return true;
218 : }
219 :
220 : bool
221 284 : BytecodeCompiler::canLazilyParse()
222 : {
223 565 : return options.canLazilyParse &&
224 575 : !(enclosingScope && enclosingScope->hasOnChain(ScopeKind::NonSyntactic)) &&
225 554 : !cx->compartment()->behaviors().disableLazyParsing() &&
226 554 : !cx->compartment()->behaviors().discardSource() &&
227 774 : !options.sourceIsLazy &&
228 497 : !cx->lcovEnabled();
229 : }
230 :
231 : bool
232 284 : BytecodeCompiler::createParser()
233 : {
234 284 : usedNames.emplace(cx);
235 284 : if (!usedNames->init())
236 0 : return false;
237 :
238 284 : if (canLazilyParse()) {
239 426 : syntaxParser.emplace(cx, alloc, options, sourceBuffer.get(), sourceBuffer.length(),
240 213 : /* foldConstants = */ false, *usedNames, nullptr, nullptr);
241 :
242 213 : if (!syntaxParser->checkOptions())
243 0 : return false;
244 : }
245 :
246 1136 : parser.emplace(cx, alloc, options, sourceBuffer.get(), sourceBuffer.length(),
247 852 : /* foldConstants = */ true, *usedNames, syntaxParser.ptrOr(nullptr), nullptr);
248 284 : parser->ss = scriptSource;
249 284 : if (!parser->checkOptions())
250 0 : return false;
251 :
252 284 : parser->tokenStream.tell(&startPosition);
253 284 : return true;
254 : }
255 :
256 : bool
257 284 : BytecodeCompiler::createSourceAndParser(const Maybe<uint32_t>& parameterListEnd /* = Nothing() */)
258 : {
259 568 : return createScriptSource(parameterListEnd) &&
260 568 : createParser();
261 : }
262 :
263 : bool
264 269 : BytecodeCompiler::createScript()
265 : {
266 269 : return createScript(0, sourceBuffer.length());
267 : }
268 :
269 : bool
270 284 : BytecodeCompiler::createScript(uint32_t toStringStart, uint32_t toStringEnd)
271 : {
272 568 : script = JSScript::Create(cx, options,
273 284 : sourceObject, /* sourceStart = */ 0, sourceBuffer.length(),
274 284 : toStringStart, toStringEnd);
275 284 : return script != nullptr;
276 : }
277 :
278 : bool
279 284 : BytecodeCompiler::emplaceEmitter(Maybe<BytecodeEmitter>& emitter, SharedContext* sharedContext)
280 : {
281 : BytecodeEmitter::EmitterMode emitterMode =
282 284 : options.selfHostingMode ? BytecodeEmitter::SelfHosting : BytecodeEmitter::Normal;
283 568 : emitter.emplace(/* parent = */ nullptr, parser.ptr(), sharedContext, script,
284 568 : /* lazyScript = */ nullptr, options.lineno, emitterMode);
285 284 : return emitter->init();
286 : }
287 :
288 : bool
289 0 : BytecodeCompiler::handleParseFailure(const Directives& newDirectives)
290 : {
291 0 : if (parser->hadAbortedSyntaxParse()) {
292 : // Hit some unrecoverable ambiguity during an inner syntax parse.
293 : // Syntax parsing has now been disabled in the parser, so retry
294 : // the parse.
295 0 : parser->clearAbortedSyntaxParse();
296 0 : } else if (parser->tokenStream.hadError() || directives == newDirectives) {
297 0 : return false;
298 : }
299 :
300 0 : parser->tokenStream.seek(startPosition);
301 :
302 : // Assignment must be monotonic to prevent reparsing iloops
303 0 : MOZ_ASSERT_IF(directives.strict(), newDirectives.strict());
304 0 : MOZ_ASSERT_IF(directives.asmJS(), newDirectives.asmJS());
305 0 : directives = newDirectives;
306 0 : return true;
307 : }
308 :
309 : bool
310 0 : BytecodeCompiler::deoptimizeArgumentsInEnclosingScripts(JSContext* cx, HandleObject environment)
311 : {
312 0 : RootedObject env(cx, environment);
313 0 : while (env->is<EnvironmentObject>() || env->is<DebugEnvironmentProxy>()) {
314 0 : if (env->is<CallObject>()) {
315 0 : RootedFunction fun(cx, &env->as<CallObject>().callee());
316 0 : RootedScript script(cx, JSFunction::getOrCreateScript(cx, fun));
317 0 : if (!script)
318 0 : return false;
319 0 : if (script->argumentsHasVarBinding()) {
320 0 : if (!JSScript::argumentsOptimizationFailed(cx, script))
321 0 : return false;
322 : }
323 : }
324 0 : env = env->enclosingEnvironment();
325 : }
326 :
327 0 : return true;
328 : }
329 :
330 : JSScript*
331 269 : BytecodeCompiler::compileScript(HandleObject environment, SharedContext* sc)
332 : {
333 269 : if (!createSourceAndParser())
334 0 : return nullptr;
335 :
336 269 : if (!createScript())
337 0 : return nullptr;
338 :
339 538 : Maybe<BytecodeEmitter> emitter;
340 269 : if (!emplaceEmitter(emitter, sc))
341 0 : return nullptr;
342 :
343 : for (;;) {
344 : ParseNode* pn;
345 269 : if (sc->isEvalContext())
346 2 : pn = parser->evalBody(sc->asEvalContext());
347 : else
348 267 : pn = parser->globalBody(sc->asGlobalContext());
349 :
350 : // Successfully parsed. Emit the script.
351 268 : if (pn) {
352 268 : if (sc->isEvalContext() && sc->hasDebuggerStatement() && !cx->helperThread()) {
353 : // If the eval'ed script contains any debugger statement, force construction
354 : // of arguments objects for the caller script and any other scripts it is
355 : // transitively nested inside. The debugger can access any variable on the
356 : // scope chain.
357 0 : if (!deoptimizeArgumentsInEnclosingScripts(cx, environment))
358 0 : return nullptr;
359 : }
360 268 : if (!emitter->emitScript(pn))
361 0 : return nullptr;
362 269 : if (!NameFunctions(cx, pn))
363 0 : return nullptr;
364 269 : parser->handler.freeTree(pn);
365 :
366 269 : break;
367 : }
368 :
369 : // Maybe we aborted a syntax parse. See if we can try again.
370 0 : if (!handleParseFailure(directives))
371 0 : return nullptr;
372 :
373 : // Reset UsedNameTracker state before trying again.
374 0 : usedNames->reset();
375 0 : }
376 :
377 : // We have just finished parsing the source. Inform the source so that we
378 : // can compute statistics (e.g. how much time our functions remain lazy).
379 269 : script->scriptSource()->recordParseEnded();
380 :
381 : // Enqueue an off-thread source compression task after finishing parsing.
382 269 : if (!scriptSource->tryCompressOffThread(cx))
383 0 : return nullptr;
384 :
385 269 : MOZ_ASSERT_IF(!cx->helperThread(), !cx->isExceptionPending());
386 :
387 269 : return script;
388 : }
389 :
390 : JSScript*
391 267 : BytecodeCompiler::compileGlobalScript(ScopeKind scopeKind)
392 : {
393 534 : GlobalSharedContext globalsc(cx, scopeKind, directives, options.extraWarningsOption);
394 534 : return compileScript(nullptr, &globalsc);
395 : }
396 :
397 : JSScript*
398 2 : BytecodeCompiler::compileEvalScript(HandleObject environment, HandleScope enclosingScope)
399 : {
400 : EvalSharedContext evalsc(cx, environment, enclosingScope,
401 4 : directives, options.extraWarningsOption);
402 4 : return compileScript(environment, &evalsc);
403 : }
404 :
405 : ModuleObject*
406 0 : BytecodeCompiler::compileModule()
407 : {
408 0 : if (!createSourceAndParser())
409 0 : return nullptr;
410 :
411 0 : Rooted<ModuleObject*> module(cx, ModuleObject::create(cx));
412 0 : if (!module)
413 0 : return nullptr;
414 :
415 0 : if (!createScript())
416 0 : return nullptr;
417 :
418 0 : module->init(script);
419 :
420 0 : ModuleBuilder builder(cx, module);
421 0 : ModuleSharedContext modulesc(cx, module, enclosingScope, builder);
422 0 : ParseNode* pn = parser->moduleBody(&modulesc);
423 0 : if (!pn)
424 0 : return nullptr;
425 :
426 0 : Maybe<BytecodeEmitter> emitter;
427 0 : if (!emplaceEmitter(emitter, &modulesc))
428 0 : return nullptr;
429 0 : if (!emitter->emitScript(pn->pn_body))
430 0 : return nullptr;
431 :
432 0 : if (!NameFunctions(cx, pn))
433 0 : return nullptr;
434 :
435 0 : parser->handler.freeTree(pn);
436 :
437 0 : if (!builder.initModule())
438 0 : return nullptr;
439 :
440 0 : RootedModuleEnvironmentObject env(cx, ModuleEnvironmentObject::create(cx, module));
441 0 : if (!env)
442 0 : return nullptr;
443 :
444 0 : module->setInitialEnvironment(env);
445 :
446 : // Enqueue an off-thread source compression task after finishing parsing.
447 0 : if (!scriptSource->tryCompressOffThread(cx))
448 0 : return nullptr;
449 :
450 0 : MOZ_ASSERT_IF(!cx->helperThread(), !cx->isExceptionPending());
451 0 : return module;
452 : }
453 :
454 : // Compile a standalone JS function, which might appear as the value of an
455 : // event handler attribute in an HTML <INPUT> tag, or in a Function()
456 : // constructor.
457 : bool
458 15 : BytecodeCompiler::compileStandaloneFunction(MutableHandleFunction fun,
459 : GeneratorKind generatorKind,
460 : FunctionAsyncKind asyncKind,
461 : const Maybe<uint32_t>& parameterListEnd)
462 : {
463 15 : MOZ_ASSERT(fun);
464 15 : MOZ_ASSERT(fun->isTenured());
465 :
466 15 : if (!createSourceAndParser(parameterListEnd))
467 0 : return false;
468 :
469 : // Speculatively parse using the default directives implied by the context.
470 : // If a directive is encountered (e.g., "use strict") that changes how the
471 : // function should have been parsed, we backup and reparse with the new set
472 : // of directives.
473 :
474 : ParseNode* fn;
475 0 : do {
476 15 : Directives newDirectives = directives;
477 30 : fn = parser->standaloneFunction(fun, enclosingScope, parameterListEnd, generatorKind,
478 15 : asyncKind, directives, &newDirectives);
479 15 : if (!fn && !handleParseFailure(newDirectives))
480 0 : return false;
481 15 : } while (!fn);
482 :
483 15 : if (fn->pn_funbox->function()->isInterpreted()) {
484 15 : MOZ_ASSERT(fun == fn->pn_funbox->function());
485 :
486 15 : if (!createScript(fn->pn_funbox->toStringStart, fn->pn_funbox->toStringEnd))
487 0 : return false;
488 :
489 30 : Maybe<BytecodeEmitter> emitter;
490 15 : if (!emplaceEmitter(emitter, fn->pn_funbox))
491 0 : return false;
492 15 : if (!emitter->emitFunctionScript(fn->pn_body))
493 0 : return false;
494 : } else {
495 0 : fun.set(fn->pn_funbox->function());
496 0 : MOZ_ASSERT(IsAsmJSModule(fun));
497 : }
498 :
499 15 : if (!NameFunctions(cx, fn))
500 0 : return false;
501 :
502 : // Enqueue an off-thread source compression task after finishing parsing.
503 15 : if (!scriptSource->tryCompressOffThread(cx))
504 0 : return false;
505 :
506 15 : return true;
507 : }
508 :
509 : ScriptSourceObject*
510 0 : BytecodeCompiler::sourceObjectPtr() const
511 : {
512 0 : return sourceObject.get();
513 : }
514 :
515 : ScriptSourceObject*
516 425 : frontend::CreateScriptSourceObject(JSContext* cx, const ReadOnlyCompileOptions& options,
517 : const Maybe<uint32_t>& parameterListEnd /* = Nothing() */)
518 : {
519 425 : ScriptSource* ss = cx->new_<ScriptSource>();
520 425 : if (!ss)
521 0 : return nullptr;
522 850 : ScriptSourceHolder ssHolder(ss);
523 :
524 425 : if (!ss->initFromOptions(cx, options, parameterListEnd))
525 0 : return nullptr;
526 :
527 850 : RootedScriptSource sso(cx, ScriptSourceObject::create(cx, ss));
528 425 : if (!sso)
529 0 : return nullptr;
530 :
531 : // Off-thread compilations do all their GC heap allocation, including the
532 : // SSO, in a temporary compartment. Hence, for the SSO to refer to the
533 : // gc-heap-allocated values in |options|, it would need cross-compartment
534 : // wrappers from the temporary compartment to the real compartment --- which
535 : // would then be inappropriate once we merged the temporary and real
536 : // compartments.
537 : //
538 : // Instead, we put off populating those SSO slots in off-thread compilations
539 : // until after we've merged compartments.
540 425 : if (!cx->helperThread()) {
541 425 : if (!ScriptSourceObject::initFromOptions(cx, sso, options))
542 0 : return nullptr;
543 : }
544 :
545 425 : return sso;
546 : }
547 :
548 : // CompileScript independently returns the ScriptSourceObject (SSO) for the
549 : // compile. This is used by off-thread script compilation (OT-SC).
550 : //
551 : // OT-SC cannot initialize the SSO when it is first constructed because the
552 : // SSO is allocated initially in a separate compartment.
553 : //
554 : // After OT-SC, the separate compartment is merged with the main compartment,
555 : // at which point the JSScripts created become observable by the debugger via
556 : // memory-space scanning.
557 : //
558 : // Whatever happens to the top-level script compilation (even if it fails and
559 : // returns null), we must finish initializing the SSO. This is because there
560 : // may be valid inner scripts observable by the debugger which reference the
561 : // partially-initialized SSO.
562 : class MOZ_STACK_CLASS AutoInitializeSourceObject
563 : {
564 : BytecodeCompiler& compiler_;
565 : ScriptSourceObject** sourceObjectOut_;
566 :
567 : public:
568 269 : AutoInitializeSourceObject(BytecodeCompiler& compiler,
569 : ScriptSourceObject** sourceObjectOut)
570 269 : : compiler_(compiler),
571 269 : sourceObjectOut_(sourceObjectOut)
572 269 : { }
573 :
574 538 : ~AutoInitializeSourceObject() {
575 269 : if (sourceObjectOut_)
576 0 : *sourceObjectOut_ = compiler_.sourceObjectPtr();
577 269 : }
578 : };
579 :
580 : JSScript*
581 267 : frontend::CompileGlobalScript(JSContext* cx, LifoAlloc& alloc, ScopeKind scopeKind,
582 : const ReadOnlyCompileOptions& options,
583 : SourceBufferHolder& srcBuf,
584 : ScriptSourceObject** sourceObjectOut)
585 : {
586 267 : MOZ_ASSERT(scopeKind == ScopeKind::Global || scopeKind == ScopeKind::NonSyntactic);
587 534 : BytecodeCompiler compiler(cx, alloc, options, srcBuf, /* enclosingScope = */ nullptr);
588 534 : AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut);
589 534 : return compiler.compileGlobalScript(scopeKind);
590 : }
591 :
592 : JSScript*
593 2 : frontend::CompileEvalScript(JSContext* cx, LifoAlloc& alloc,
594 : HandleObject environment, HandleScope enclosingScope,
595 : const ReadOnlyCompileOptions& options,
596 : SourceBufferHolder& srcBuf,
597 : ScriptSourceObject** sourceObjectOut)
598 : {
599 4 : BytecodeCompiler compiler(cx, alloc, options, srcBuf, enclosingScope);
600 4 : AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut);
601 4 : return compiler.compileEvalScript(environment, enclosingScope);
602 : }
603 :
604 : ModuleObject*
605 0 : frontend::CompileModule(JSContext* cx, const ReadOnlyCompileOptions& optionsInput,
606 : SourceBufferHolder& srcBuf, LifoAlloc& alloc,
607 : ScriptSourceObject** sourceObjectOut)
608 : {
609 0 : MOZ_ASSERT(srcBuf.get());
610 0 : MOZ_ASSERT_IF(sourceObjectOut, *sourceObjectOut == nullptr);
611 :
612 0 : CompileOptions options(cx, optionsInput);
613 0 : options.maybeMakeStrictMode(true); // ES6 10.2.1 Module code is always strict mode code.
614 0 : options.setIsRunOnce(true);
615 0 : options.allowHTMLComments = false;
616 :
617 0 : RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope());
618 0 : BytecodeCompiler compiler(cx, alloc, options, srcBuf, emptyGlobalScope);
619 0 : AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut);
620 0 : return compiler.compileModule();
621 : }
622 :
623 : ModuleObject*
624 0 : frontend::CompileModule(JSContext* cx, const ReadOnlyCompileOptions& options,
625 : SourceBufferHolder& srcBuf)
626 : {
627 0 : if (!GlobalObject::ensureModulePrototypesCreated(cx, cx->global()))
628 0 : return nullptr;
629 :
630 0 : LifoAlloc& alloc = cx->tempLifoAlloc();
631 0 : RootedModuleObject module(cx, CompileModule(cx, options, srcBuf, alloc));
632 0 : if (!module)
633 0 : return nullptr;
634 :
635 : // This happens in GlobalHelperThreadState::finishModuleParseTask() when a
636 : // module is compiled off thread.
637 0 : if (!ModuleObject::Freeze(cx, module))
638 0 : return nullptr;
639 :
640 0 : return module;
641 : }
642 :
643 : bool
644 1407 : frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const char16_t* chars, size_t length)
645 : {
646 1407 : MOZ_ASSERT(cx->compartment() == lazy->functionNonDelazifying()->compartment());
647 :
648 1407 : uint32_t sourceStartColumn = lazy->scriptSource()->startColumn();
649 2814 : CompileOptions options(cx, lazy->version());
650 1407 : options.setMutedErrors(lazy->mutedErrors())
651 2814 : .setFileAndLine(lazy->filename(), lazy->lineno())
652 2814 : .setColumn(lazy->column(), sourceStartColumn)
653 1407 : .setNoScriptRval(false)
654 1407 : .setSelfHostingMode(false);
655 :
656 : // Update statistics to find out if we are delazifying just after having
657 : // lazified. Note that we are interested in the delta between end of
658 : // syntax parsing and start of full parsing, so we do this now rather than
659 : // after parsing below.
660 1407 : if (!lazy->scriptSource()->parseEnded().IsNull()) {
661 1278 : const mozilla::TimeDuration delta = mozilla::TimeStamp::Now() -
662 1917 : lazy->scriptSource()->parseEnded();
663 :
664 : // Differentiate between web-facing and privileged code, to aid
665 : // with optimization. Due to the number of calls to this function,
666 : // we use `cx->runningWithTrustedPrincipals`, which is fast but
667 : // will classify addons alongside with web-facing code.
668 639 : const int HISTOGRAM = cx->runningWithTrustedPrincipals()
669 639 : ? JS_TELEMETRY_PRIVILEGED_PARSER_COMPILE_LAZY_AFTER_MS
670 639 : : JS_TELEMETRY_WEB_PARSER_COMPILE_LAZY_AFTER_MS;
671 639 : cx->runtime()->addTelemetry(HISTOGRAM, delta.ToMilliseconds());
672 : }
673 :
674 2814 : UsedNameTracker usedNames(cx);
675 1407 : if (!usedNames.init())
676 0 : return false;
677 : Parser<FullParseHandler, char16_t> parser(cx, cx->tempLifoAlloc(), options, chars, length,
678 : /* foldConstants = */ true, usedNames, nullptr,
679 2814 : lazy);
680 1407 : if (!parser.checkOptions())
681 0 : return false;
682 :
683 2814 : Rooted<JSFunction*> fun(cx, lazy->functionNonDelazifying());
684 1407 : MOZ_ASSERT(!lazy->isLegacyGenerator());
685 5628 : ParseNode* pn = parser.standaloneLazyFunction(fun, lazy->toStringStart() + sourceStartColumn,
686 5628 : lazy->strict(), lazy->generatorKind(), lazy->asyncKind());
687 1407 : if (!pn)
688 0 : return false;
689 :
690 2814 : RootedScriptSource sourceObject(cx, lazy->sourceObject());
691 1407 : MOZ_ASSERT(sourceObject);
692 :
693 7035 : Rooted<JSScript*> script(cx, JSScript::Create(cx, options, sourceObject,
694 2814 : lazy->begin(), lazy->end(),
695 5628 : lazy->toStringStart(), lazy->toStringEnd()));
696 1407 : if (!script)
697 0 : return false;
698 :
699 1407 : if (lazy->isLikelyConstructorWrapper())
700 0 : script->setLikelyConstructorWrapper();
701 1407 : if (lazy->hasBeenCloned())
702 22 : script->setHasBeenCloned();
703 :
704 2814 : BytecodeEmitter bce(/* parent = */ nullptr, &parser, pn->pn_funbox, script, lazy,
705 5628 : pn->pn_pos, BytecodeEmitter::LazyFunction);
706 1407 : if (!bce.init())
707 0 : return false;
708 :
709 1407 : if (!bce.emitFunctionScript(pn->pn_body))
710 0 : return false;
711 :
712 1407 : if (!NameFunctions(cx, pn))
713 0 : return false;
714 :
715 1407 : return true;
716 : }
717 :
718 : bool
719 15 : frontend::CompileStandaloneFunction(JSContext* cx, MutableHandleFunction fun,
720 : const ReadOnlyCompileOptions& options,
721 : JS::SourceBufferHolder& srcBuf,
722 : const Maybe<uint32_t>& parameterListEnd,
723 : HandleScope enclosingScope /* = nullptr */)
724 : {
725 30 : RootedScope scope(cx, enclosingScope);
726 15 : if (!scope)
727 4 : scope = &cx->global()->emptyGlobalScope();
728 :
729 30 : BytecodeCompiler compiler(cx, cx->tempLifoAlloc(), options, srcBuf, scope);
730 30 : return compiler.compileStandaloneFunction(fun, NotGenerator, SyncFunction, parameterListEnd);
731 : }
732 :
733 : bool
734 0 : frontend::CompileStandaloneGenerator(JSContext* cx, MutableHandleFunction fun,
735 : const ReadOnlyCompileOptions& options,
736 : JS::SourceBufferHolder& srcBuf,
737 : const Maybe<uint32_t>& parameterListEnd)
738 : {
739 0 : RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope());
740 :
741 0 : BytecodeCompiler compiler(cx, cx->tempLifoAlloc(), options, srcBuf, emptyGlobalScope);
742 0 : return compiler.compileStandaloneFunction(fun, StarGenerator, SyncFunction, parameterListEnd);
743 : }
744 :
745 : bool
746 0 : frontend::CompileStandaloneAsyncFunction(JSContext* cx, MutableHandleFunction fun,
747 : const ReadOnlyCompileOptions& options,
748 : JS::SourceBufferHolder& srcBuf,
749 : const Maybe<uint32_t>& parameterListEnd)
750 : {
751 0 : RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope());
752 :
753 0 : BytecodeCompiler compiler(cx, cx->tempLifoAlloc(), options, srcBuf, emptyGlobalScope);
754 0 : return compiler.compileStandaloneFunction(fun, NotGenerator, AsyncFunction, parameterListEnd);
755 : }
756 :
757 : bool
758 0 : frontend::CompileStandaloneAsyncGenerator(JSContext* cx, MutableHandleFunction fun,
759 : const ReadOnlyCompileOptions& options,
760 : JS::SourceBufferHolder& srcBuf,
761 : const Maybe<uint32_t>& parameterListEnd)
762 : {
763 0 : RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope());
764 :
765 0 : BytecodeCompiler compiler(cx, cx->tempLifoAlloc(), options, srcBuf, emptyGlobalScope);
766 0 : return compiler.compileStandaloneFunction(fun, StarGenerator, AsyncFunction, parameterListEnd);
767 : }
|