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/EmitterScope.h"
8
9 #include "frontend/AbstractScopePtr.h"
10 #include "frontend/BytecodeEmitter.h"
11 #include "frontend/ModuleSharedContext.h"
12 #include "frontend/TDZCheckCache.h"
13 #include "vm/GlobalObject.h"
14
15 using namespace js;
16 using namespace js::frontend;
17
18 using mozilla::DebugOnly;
19 using mozilla::Maybe;
20 using mozilla::Nothing;
21 using mozilla::Some;
22
EmitterScope(BytecodeEmitter * bce)23 EmitterScope::EmitterScope(BytecodeEmitter* bce)
24 : Nestable<EmitterScope>(&bce->innermostEmitterScope_),
25 nameCache_(bce->cx->frontendCollectionPool()),
26 hasEnvironment_(false),
27 environmentChainLength_(0),
28 nextFrameSlot_(0),
29 scopeIndex_(ScopeNote::NoScopeIndex),
30 noteIndex_(ScopeNote::NoScopeNoteIndex) {}
31
ensureCache(BytecodeEmitter * bce)32 bool EmitterScope::ensureCache(BytecodeEmitter* bce) {
33 return nameCache_.acquire(bce->cx);
34 }
35
checkSlotLimits(BytecodeEmitter * bce,const BindingIter & bi)36 bool EmitterScope::checkSlotLimits(BytecodeEmitter* bce,
37 const BindingIter& bi) {
38 if (bi.nextFrameSlot() >= LOCALNO_LIMIT ||
39 bi.nextEnvironmentSlot() >= ENVCOORD_SLOT_LIMIT) {
40 bce->reportError(nullptr, JSMSG_TOO_MANY_LOCALS);
41 return false;
42 }
43 return true;
44 }
45
checkEnvironmentChainLength(BytecodeEmitter * bce)46 bool EmitterScope::checkEnvironmentChainLength(BytecodeEmitter* bce) {
47 uint32_t hops;
48 if (EmitterScope* emitterScope = enclosing(&bce)) {
49 hops = emitterScope->environmentChainLength_;
50 } else {
51 hops = bce->sc->compilationEnclosingScope()->environmentChainLength();
52 }
53
54 if (hops >= ENVCOORD_HOPS_LIMIT - 1) {
55 bce->reportError(nullptr, JSMSG_TOO_DEEP, js_function_str);
56 return false;
57 }
58
59 environmentChainLength_ = mozilla::AssertedCast<uint8_t>(hops + 1);
60 return true;
61 }
62
updateFrameFixedSlots(BytecodeEmitter * bce,const BindingIter & bi)63 void EmitterScope::updateFrameFixedSlots(BytecodeEmitter* bce,
64 const BindingIter& bi) {
65 nextFrameSlot_ = bi.nextFrameSlot();
66 if (nextFrameSlot_ > bce->maxFixedSlots) {
67 bce->maxFixedSlots = nextFrameSlot_;
68 }
69 MOZ_ASSERT_IF(
70 bce->sc->isFunctionBox() && (bce->sc->asFunctionBox()->isGenerator() ||
71 bce->sc->asFunctionBox()->isAsync()),
72 bce->maxFixedSlots == 0);
73 }
74
putNameInCache(BytecodeEmitter * bce,JSAtom * name,NameLocation loc)75 bool EmitterScope::putNameInCache(BytecodeEmitter* bce, JSAtom* name,
76 NameLocation loc) {
77 NameLocationMap& cache = *nameCache_;
78 NameLocationMap::AddPtr p = cache.lookupForAdd(name);
79 MOZ_ASSERT(!p);
80 if (!cache.add(p, name, loc)) {
81 ReportOutOfMemory(bce->cx);
82 return false;
83 }
84 return true;
85 }
86
lookupInCache(BytecodeEmitter * bce,JSAtom * name)87 Maybe<NameLocation> EmitterScope::lookupInCache(BytecodeEmitter* bce,
88 JSAtom* name) {
89 if (NameLocationMap::Ptr p = nameCache_->lookup(name)) {
90 return Some(p->value().wrapped);
91 }
92 if (fallbackFreeNameLocation_ && nameCanBeFree(bce, name)) {
93 return fallbackFreeNameLocation_;
94 }
95 return Nothing();
96 }
97
enclosing(BytecodeEmitter ** bce) const98 EmitterScope* EmitterScope::enclosing(BytecodeEmitter** bce) const {
99 // There is an enclosing scope with access to the same frame.
100 if (EmitterScope* inFrame = enclosingInFrame()) {
101 return inFrame;
102 }
103
104 // We are currently compiling the enclosing script, look in the
105 // enclosing BCE.
106 if ((*bce)->parent) {
107 *bce = (*bce)->parent;
108 return (*bce)->innermostEmitterScopeNoCheck();
109 }
110
111 return nullptr;
112 }
113
enclosingScope(BytecodeEmitter * bce) const114 AbstractScopePtr EmitterScope::enclosingScope(BytecodeEmitter* bce) const {
115 if (EmitterScope* es = enclosing(&bce)) {
116 return es->scope(bce);
117 }
118
119 // The enclosing script is already compiled or the current script is the
120 // global script.
121 return AbstractScopePtr(bce->sc->compilationEnclosingScope());
122 }
123
124 /* static */
nameCanBeFree(BytecodeEmitter * bce,JSAtom * name)125 bool EmitterScope::nameCanBeFree(BytecodeEmitter* bce, JSAtom* name) {
126 // '.generator' cannot be accessed by name.
127 return name != bce->cx->names().dotGenerator;
128 }
129
130 #ifdef DEBUG
NameIsOnEnvironment(Scope * scope,JSAtom * name)131 static bool NameIsOnEnvironment(Scope* scope, JSAtom* name) {
132 for (BindingIter bi(scope); bi; bi++) {
133 // If found, the name must already be on the environment or an import,
134 // or else there is a bug in the closed-over name analysis in the
135 // Parser.
136 if (bi.name() == name) {
137 BindingLocation::Kind kind = bi.location().kind();
138
139 if (bi.hasArgumentSlot()) {
140 JSScript* script = scope->as<FunctionScope>().script();
141 if (script->functionAllowsParameterRedeclaration()) {
142 // Check for duplicate positional formal parameters.
143 for (BindingIter bi2(bi); bi2 && bi2.hasArgumentSlot(); bi2++) {
144 if (bi2.name() == name) {
145 kind = bi2.location().kind();
146 }
147 }
148 }
149 }
150
151 return kind == BindingLocation::Kind::Global ||
152 kind == BindingLocation::Kind::Environment ||
153 kind == BindingLocation::Kind::Import;
154 }
155 }
156
157 // If not found, assume it's on the global or dynamically accessed.
158 return true;
159 }
160 #endif
161
162 /* static */
searchInEnclosingScope(JSAtom * name,Scope * scope,uint8_t hops)163 NameLocation EmitterScope::searchInEnclosingScope(JSAtom* name, Scope* scope,
164 uint8_t hops) {
165 for (ScopeIter si(scope); si; si++) {
166 MOZ_ASSERT(NameIsOnEnvironment(si.scope(), name));
167
168 bool hasEnv = si.hasSyntacticEnvironment();
169
170 switch (si.kind()) {
171 case ScopeKind::Function:
172 if (hasEnv) {
173 JSScript* script = si.scope()->as<FunctionScope>().script();
174 if (script->funHasExtensibleScope()) {
175 return NameLocation::Dynamic();
176 }
177
178 for (BindingIter bi(si.scope()); bi; bi++) {
179 if (bi.name() != name) {
180 continue;
181 }
182
183 BindingLocation bindLoc = bi.location();
184 if (bi.hasArgumentSlot() &&
185 script->functionAllowsParameterRedeclaration()) {
186 // Check for duplicate positional formal parameters.
187 for (BindingIter bi2(bi); bi2 && bi2.hasArgumentSlot(); bi2++) {
188 if (bi2.name() == name) {
189 bindLoc = bi2.location();
190 }
191 }
192 }
193
194 MOZ_ASSERT(bindLoc.kind() == BindingLocation::Kind::Environment);
195 return NameLocation::EnvironmentCoordinate(bi.kind(), hops,
196 bindLoc.slot());
197 }
198 }
199 break;
200
201 case ScopeKind::FunctionBodyVar:
202 case ScopeKind::Lexical:
203 case ScopeKind::NamedLambda:
204 case ScopeKind::StrictNamedLambda:
205 case ScopeKind::SimpleCatch:
206 case ScopeKind::Catch:
207 case ScopeKind::FunctionLexical:
208 if (hasEnv) {
209 for (BindingIter bi(si.scope()); bi; bi++) {
210 if (bi.name() != name) {
211 continue;
212 }
213
214 // The name must already have been marked as closed
215 // over. If this assertion is hit, there is a bug in the
216 // name analysis.
217 BindingLocation bindLoc = bi.location();
218 MOZ_ASSERT(bindLoc.kind() == BindingLocation::Kind::Environment);
219 return NameLocation::EnvironmentCoordinate(bi.kind(), hops,
220 bindLoc.slot());
221 }
222 }
223 break;
224
225 case ScopeKind::Module:
226 if (hasEnv) {
227 for (BindingIter bi(si.scope()); bi; bi++) {
228 if (bi.name() != name) {
229 continue;
230 }
231
232 BindingLocation bindLoc = bi.location();
233
234 // Imports are on the environment but are indirect
235 // bindings and must be accessed dynamically instead of
236 // using an EnvironmentCoordinate.
237 if (bindLoc.kind() == BindingLocation::Kind::Import) {
238 MOZ_ASSERT(si.kind() == ScopeKind::Module);
239 return NameLocation::Import();
240 }
241
242 MOZ_ASSERT(bindLoc.kind() == BindingLocation::Kind::Environment);
243 return NameLocation::EnvironmentCoordinate(bi.kind(), hops,
244 bindLoc.slot());
245 }
246 }
247 break;
248
249 case ScopeKind::Eval:
250 case ScopeKind::StrictEval:
251 // As an optimization, if the eval doesn't have its own var
252 // environment and its immediate enclosing scope is a global
253 // scope, all accesses are global.
254 if (!hasEnv && si.scope()->enclosing()->is<GlobalScope>()) {
255 return NameLocation::Global(BindingKind::Var);
256 }
257 return NameLocation::Dynamic();
258
259 case ScopeKind::Global:
260 return NameLocation::Global(BindingKind::Var);
261
262 case ScopeKind::With:
263 case ScopeKind::NonSyntactic:
264 return NameLocation::Dynamic();
265
266 case ScopeKind::WasmInstance:
267 case ScopeKind::WasmFunction:
268 MOZ_CRASH("No direct eval inside wasm functions");
269 }
270
271 if (hasEnv) {
272 MOZ_ASSERT(hops < ENVCOORD_HOPS_LIMIT - 1);
273 hops++;
274 }
275 }
276
277 MOZ_CRASH("Malformed scope chain");
278 }
279
searchAndCache(BytecodeEmitter * bce,JSAtom * name)280 NameLocation EmitterScope::searchAndCache(BytecodeEmitter* bce, JSAtom* name) {
281 Maybe<NameLocation> loc;
282 uint8_t hops = hasEnvironment() ? 1 : 0;
283 DebugOnly<bool> inCurrentScript = enclosingInFrame();
284
285 // Start searching in the current compilation.
286 for (EmitterScope* es = enclosing(&bce); es; es = es->enclosing(&bce)) {
287 loc = es->lookupInCache(bce, name);
288 if (loc) {
289 if (loc->kind() == NameLocation::Kind::EnvironmentCoordinate) {
290 *loc = loc->addHops(hops);
291 }
292 break;
293 }
294
295 if (es->hasEnvironment()) {
296 hops++;
297 }
298
299 #ifdef DEBUG
300 if (!es->enclosingInFrame()) {
301 inCurrentScript = false;
302 }
303 #endif
304 }
305
306 // If the name is not found in the current compilation, walk the Scope
307 // chain encompassing the compilation.
308 if (!loc) {
309 inCurrentScript = false;
310 loc = Some(searchInEnclosingScope(
311 name, bce->sc->compilationEnclosingScope(), hops));
312 }
313
314 // Each script has its own frame. A free name that is accessed
315 // from an inner script must not be a frame slot access. If this
316 // assertion is hit, it is a bug in the free name analysis in the
317 // parser.
318 MOZ_ASSERT_IF(!inCurrentScript, loc->kind() != NameLocation::Kind::FrameSlot);
319
320 // It is always correct to not cache the location. Ignore OOMs to make
321 // lookups infallible.
322 if (!putNameInCache(bce, name, *loc)) {
323 bce->cx->recoverFromOutOfMemory();
324 }
325
326 return *loc;
327 }
328
internEmptyGlobalScopeAsBody(BytecodeEmitter * bce)329 bool EmitterScope::internEmptyGlobalScopeAsBody(BytecodeEmitter* bce) {
330 Scope* scope = &bce->cx->global()->emptyGlobalScope();
331 hasEnvironment_ = scope->hasEnvironment();
332
333 bce->bodyScopeIndex = bce->perScriptData().gcThingList().length();
334 return bce->perScriptData().gcThingList().appendEmptyGlobalScope(
335 &scopeIndex_);
336 }
337
338 template <typename ScopeCreator>
internScopeCreationData(BytecodeEmitter * bce,ScopeCreator createScope)339 bool EmitterScope::internScopeCreationData(BytecodeEmitter* bce,
340 ScopeCreator createScope) {
341 Rooted<AbstractScopePtr> enclosing(bce->cx, enclosingScope(bce));
342 ScopeIndex index;
343 if (!createScope(bce->cx, enclosing, &index)) {
344 return false;
345 }
346 auto scope = bce->compilationInfo.scopeCreationData[index.index];
347 hasEnvironment_ = scope.get().hasEnvironment();
348 return bce->perScriptData().gcThingList().append(index, &scopeIndex_);
349 }
350
351 template <typename ScopeCreator>
internBodyScopeCreationData(BytecodeEmitter * bce,ScopeCreator createScope)352 bool EmitterScope::internBodyScopeCreationData(BytecodeEmitter* bce,
353 ScopeCreator createScope) {
354 MOZ_ASSERT(bce->bodyScopeIndex == UINT32_MAX,
355 "There can be only one body scope");
356 bce->bodyScopeIndex = bce->perScriptData().gcThingList().length();
357 return internScopeCreationData(bce, createScope);
358 }
359
appendScopeNote(BytecodeEmitter * bce)360 bool EmitterScope::appendScopeNote(BytecodeEmitter* bce) {
361 MOZ_ASSERT(ScopeKindIsInBody(scope(bce).kind()) && enclosingInFrame(),
362 "Scope notes are not needed for body-level scopes.");
363 noteIndex_ = bce->bytecodeSection().scopeNoteList().length();
364 return bce->bytecodeSection().scopeNoteList().append(
365 index(), bce->bytecodeSection().offset(),
366 enclosingInFrame() ? enclosingInFrame()->noteIndex()
367 : ScopeNote::NoScopeNoteIndex);
368 }
369
deadZoneFrameSlotRange(BytecodeEmitter * bce,uint32_t slotStart,uint32_t slotEnd) const370 bool EmitterScope::deadZoneFrameSlotRange(BytecodeEmitter* bce,
371 uint32_t slotStart,
372 uint32_t slotEnd) const {
373 // Lexical bindings throw ReferenceErrors if they are used before
374 // initialization. See ES6 8.1.1.1.6.
375 //
376 // For completeness, lexical bindings are initialized in ES6 by calling
377 // InitializeBinding, after which touching the binding will no longer
378 // throw reference errors. See 13.1.11, 9.2.13, 13.6.3.4, 13.6.4.6,
379 // 13.6.4.8, 13.14.5, 15.1.8, and 15.2.0.15.
380 if (slotStart != slotEnd) {
381 if (!bce->emit1(JSOp::Uninitialized)) {
382 return false;
383 }
384 for (uint32_t slot = slotStart; slot < slotEnd; slot++) {
385 if (!bce->emitLocalOp(JSOp::InitLexical, slot)) {
386 return false;
387 }
388 }
389 if (!bce->emit1(JSOp::Pop)) {
390 return false;
391 }
392 }
393
394 return true;
395 }
396
dump(BytecodeEmitter * bce)397 void EmitterScope::dump(BytecodeEmitter* bce) {
398 fprintf(stdout, "EmitterScope [%s] %p\n", ScopeKindString(scope(bce).kind()),
399 this);
400
401 for (NameLocationMap::Range r = nameCache_->all(); !r.empty(); r.popFront()) {
402 const NameLocation& l = r.front().value();
403
404 UniqueChars bytes = AtomToPrintableString(bce->cx, r.front().key());
405 if (!bytes) {
406 return;
407 }
408 if (l.kind() != NameLocation::Kind::Dynamic) {
409 fprintf(stdout, " %s %s ", BindingKindString(l.bindingKind()),
410 bytes.get());
411 } else {
412 fprintf(stdout, " %s ", bytes.get());
413 }
414
415 switch (l.kind()) {
416 case NameLocation::Kind::Dynamic:
417 fprintf(stdout, "dynamic\n");
418 break;
419 case NameLocation::Kind::Global:
420 fprintf(stdout, "global\n");
421 break;
422 case NameLocation::Kind::Intrinsic:
423 fprintf(stdout, "intrinsic\n");
424 break;
425 case NameLocation::Kind::NamedLambdaCallee:
426 fprintf(stdout, "named lambda callee\n");
427 break;
428 case NameLocation::Kind::Import:
429 fprintf(stdout, "import\n");
430 break;
431 case NameLocation::Kind::ArgumentSlot:
432 fprintf(stdout, "arg slot=%u\n", l.argumentSlot());
433 break;
434 case NameLocation::Kind::FrameSlot:
435 fprintf(stdout, "frame slot=%u\n", l.frameSlot());
436 break;
437 case NameLocation::Kind::EnvironmentCoordinate:
438 fprintf(stdout, "environment hops=%u slot=%u\n",
439 l.environmentCoordinate().hops(),
440 l.environmentCoordinate().slot());
441 break;
442 case NameLocation::Kind::DynamicAnnexBVar:
443 fprintf(stdout, "dynamic annex b var\n");
444 break;
445 }
446 }
447
448 fprintf(stdout, "\n");
449 }
450
enterLexical(BytecodeEmitter * bce,ScopeKind kind,Handle<LexicalScope::Data * > bindings)451 bool EmitterScope::enterLexical(BytecodeEmitter* bce, ScopeKind kind,
452 Handle<LexicalScope::Data*> bindings) {
453 MOZ_ASSERT(kind != ScopeKind::NamedLambda &&
454 kind != ScopeKind::StrictNamedLambda);
455 MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck());
456
457 if (!ensureCache(bce)) {
458 return false;
459 }
460
461 // Resolve bindings.
462 TDZCheckCache* tdzCache = bce->innermostTDZCheckCache;
463 uint32_t firstFrameSlot = frameSlotStart();
464 BindingIter bi(*bindings, firstFrameSlot, /* isNamedLambda = */ false);
465 for (; bi; bi++) {
466 if (!checkSlotLimits(bce, bi)) {
467 return false;
468 }
469
470 NameLocation loc = NameLocation::fromBinding(bi.kind(), bi.location());
471 if (!putNameInCache(bce, bi.name(), loc)) {
472 return false;
473 }
474
475 if (!tdzCache->noteTDZCheck(bce, bi.name(), CheckTDZ)) {
476 return false;
477 }
478 }
479
480 updateFrameFixedSlots(bce, bi);
481
482 auto createScope = [kind, bindings, firstFrameSlot, bce](
483 JSContext* cx, Handle<AbstractScopePtr> enclosing,
484 ScopeIndex* index) {
485 return ScopeCreationData::create(cx, bce->compilationInfo, kind, bindings,
486 firstFrameSlot, enclosing, index);
487 };
488 if (!internScopeCreationData(bce, createScope)) {
489 return false;
490 }
491
492 if (ScopeKindIsInBody(kind) && hasEnvironment()) {
493 // After interning the VM scope we can get the scope index.
494 if (!bce->emitInternedScopeOp(index(), JSOp::PushLexicalEnv)) {
495 return false;
496 }
497 }
498
499 // Lexical scopes need notes to be mapped from a pc.
500 if (!appendScopeNote(bce)) {
501 return false;
502 }
503
504 // Put frame slots in TDZ. Environment slots are poisoned during
505 // environment creation.
506 //
507 // This must be done after appendScopeNote to be considered in the extent
508 // of the scope.
509 if (!deadZoneFrameSlotRange(bce, firstFrameSlot, frameSlotEnd())) {
510 return false;
511 }
512
513 return checkEnvironmentChainLength(bce);
514 }
515
enterNamedLambda(BytecodeEmitter * bce,FunctionBox * funbox)516 bool EmitterScope::enterNamedLambda(BytecodeEmitter* bce, FunctionBox* funbox) {
517 MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck());
518 MOZ_ASSERT(funbox->namedLambdaBindings());
519
520 if (!ensureCache(bce)) {
521 return false;
522 }
523
524 BindingIter bi(*funbox->namedLambdaBindings(), LOCALNO_LIMIT,
525 /* isNamedLambda = */ true);
526 MOZ_ASSERT(bi.kind() == BindingKind::NamedLambdaCallee);
527
528 // The lambda name, if not closed over, is accessed via JSOp::Callee and
529 // not a frame slot. Do not update frame slot information.
530 NameLocation loc = NameLocation::fromBinding(bi.kind(), bi.location());
531 if (!putNameInCache(bce, bi.name(), loc)) {
532 return false;
533 }
534
535 bi++;
536 MOZ_ASSERT(!bi, "There should be exactly one binding in a NamedLambda scope");
537
538 ScopeKind scopeKind =
539 funbox->strict() ? ScopeKind::StrictNamedLambda : ScopeKind::NamedLambda;
540
541 auto createScope = [funbox, scopeKind, bce](
542 JSContext* cx, Handle<AbstractScopePtr> enclosing,
543 ScopeIndex* index) {
544 return ScopeCreationData::create(cx, bce->compilationInfo, scopeKind,
545 funbox->namedLambdaBindings(),
546 LOCALNO_LIMIT, enclosing, index);
547 };
548 if (!internScopeCreationData(bce, createScope)) {
549 return false;
550 }
551
552 return checkEnvironmentChainLength(bce);
553 }
554
enterFunction(BytecodeEmitter * bce,FunctionBox * funbox)555 bool EmitterScope::enterFunction(BytecodeEmitter* bce, FunctionBox* funbox) {
556 MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck());
557
558 // If there are parameter expressions, there is an extra var scope.
559 if (!funbox->functionHasExtraBodyVarScope()) {
560 bce->setVarEmitterScope(this);
561 }
562
563 if (!ensureCache(bce)) {
564 return false;
565 }
566
567 // Resolve body-level bindings, if there are any.
568 auto bindings = funbox->functionScopeBindings();
569 if (bindings) {
570 NameLocationMap& cache = *nameCache_;
571
572 BindingIter bi(*bindings, funbox->hasParameterExprs);
573 for (; bi; bi++) {
574 if (!checkSlotLimits(bce, bi)) {
575 return false;
576 }
577
578 NameLocation loc = NameLocation::fromBinding(bi.kind(), bi.location());
579 NameLocationMap::AddPtr p = cache.lookupForAdd(bi.name());
580
581 // The only duplicate bindings that occur are simple formal
582 // parameters, in which case the last position counts, so update the
583 // location.
584 if (p) {
585 MOZ_ASSERT(bi.kind() == BindingKind::FormalParameter);
586 MOZ_ASSERT(!funbox->hasDestructuringArgs);
587 MOZ_ASSERT(!funbox->hasRest());
588 p->value() = loc;
589 continue;
590 }
591
592 if (!cache.add(p, bi.name(), loc)) {
593 ReportOutOfMemory(bce->cx);
594 return false;
595 }
596 }
597
598 updateFrameFixedSlots(bce, bi);
599 } else {
600 nextFrameSlot_ = 0;
601 }
602
603 // If the function's scope may be extended at runtime due to sloppy direct
604 // eval, any names beyond the function scope must be accessed dynamically as
605 // we don't know if the name will become a 'var' binding due to direct eval.
606 if (funbox->funHasExtensibleScope()) {
607 fallbackFreeNameLocation_ = Some(NameLocation::Dynamic());
608 }
609
610 // In case of parameter expressions, the parameters are lexical
611 // bindings and have TDZ.
612 if (funbox->hasParameterExprs && nextFrameSlot_) {
613 uint32_t paramFrameSlotEnd = 0;
614 for (BindingIter bi(*bindings, true); bi; bi++) {
615 if (!BindingKindIsLexical(bi.kind())) {
616 break;
617 }
618
619 NameLocation loc = NameLocation::fromBinding(bi.kind(), bi.location());
620 if (loc.kind() == NameLocation::Kind::FrameSlot) {
621 MOZ_ASSERT(paramFrameSlotEnd <= loc.frameSlot());
622 paramFrameSlotEnd = loc.frameSlot() + 1;
623 }
624 }
625
626 if (!deadZoneFrameSlotRange(bce, 0, paramFrameSlotEnd)) {
627 return false;
628 }
629 }
630
631 auto createScope = [funbox, bce](JSContext* cx,
632 Handle<AbstractScopePtr> enclosing,
633 ScopeIndex* index) {
634 return ScopeCreationData::create(
635 cx, bce->compilationInfo, funbox->functionScopeBindings(),
636 funbox->hasParameterExprs,
637 funbox->needsCallObjectRegardlessOfBindings(), funbox, enclosing,
638 index);
639 };
640 if (!internBodyScopeCreationData(bce, createScope)) {
641 return false;
642 }
643
644 return checkEnvironmentChainLength(bce);
645 }
646
enterFunctionExtraBodyVar(BytecodeEmitter * bce,FunctionBox * funbox)647 bool EmitterScope::enterFunctionExtraBodyVar(BytecodeEmitter* bce,
648 FunctionBox* funbox) {
649 MOZ_ASSERT(funbox->hasParameterExprs);
650 MOZ_ASSERT(funbox->extraVarScopeBindings() ||
651 funbox->needsExtraBodyVarEnvironmentRegardlessOfBindings());
652 MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck());
653
654 // The extra var scope is never popped once it's entered. It replaces the
655 // function scope as the var emitter scope.
656 bce->setVarEmitterScope(this);
657
658 if (!ensureCache(bce)) {
659 return false;
660 }
661
662 // Resolve body-level bindings, if there are any.
663 uint32_t firstFrameSlot = frameSlotStart();
664 if (auto bindings = funbox->extraVarScopeBindings()) {
665 BindingIter bi(*bindings, firstFrameSlot);
666 for (; bi; bi++) {
667 if (!checkSlotLimits(bce, bi)) {
668 return false;
669 }
670
671 NameLocation loc = NameLocation::fromBinding(bi.kind(), bi.location());
672 if (!putNameInCache(bce, bi.name(), loc)) {
673 return false;
674 }
675 }
676
677 updateFrameFixedSlots(bce, bi);
678 } else {
679 nextFrameSlot_ = firstFrameSlot;
680 }
681
682 // If the extra var scope may be extended at runtime due to sloppy
683 // direct eval, any names beyond the var scope must be accessed
684 // dynamically as we don't know if the name will become a 'var' binding
685 // due to direct eval.
686 if (funbox->funHasExtensibleScope()) {
687 fallbackFreeNameLocation_ = Some(NameLocation::Dynamic());
688 }
689
690 // Create and intern the VM scope.
691 auto createScope = [funbox, firstFrameSlot, bce](
692 JSContext* cx, Handle<AbstractScopePtr> enclosing,
693 ScopeIndex* index) {
694 return ScopeCreationData::create(
695 cx, bce->compilationInfo, ScopeKind::FunctionBodyVar,
696 funbox->extraVarScopeBindings(), firstFrameSlot,
697 funbox->needsExtraBodyVarEnvironmentRegardlessOfBindings(), enclosing,
698 index);
699 };
700 if (!internScopeCreationData(bce, createScope)) {
701 return false;
702 }
703
704 if (hasEnvironment()) {
705 if (!bce->emitInternedScopeOp(index(), JSOp::PushVarEnv)) {
706 return false;
707 }
708 }
709
710 // The extra var scope needs a note to be mapped from a pc.
711 if (!appendScopeNote(bce)) {
712 return false;
713 }
714
715 return checkEnvironmentChainLength(bce);
716 }
717
718 class DynamicBindingIter : public BindingIter {
719 public:
DynamicBindingIter(GlobalSharedContext * sc)720 explicit DynamicBindingIter(GlobalSharedContext* sc)
721 : BindingIter(*sc->bindings) {}
722
DynamicBindingIter(EvalSharedContext * sc)723 explicit DynamicBindingIter(EvalSharedContext* sc)
724 : BindingIter(*sc->bindings, /* strict = */ false) {
725 MOZ_ASSERT(!sc->strict());
726 }
727
bindingOp() const728 JSOp bindingOp() const {
729 switch (kind()) {
730 case BindingKind::Var:
731 return JSOp::DefVar;
732 case BindingKind::Let:
733 return JSOp::DefLet;
734 case BindingKind::Const:
735 return JSOp::DefConst;
736 default:
737 MOZ_CRASH("Bad BindingKind");
738 }
739 }
740 };
741
enterGlobal(BytecodeEmitter * bce,GlobalSharedContext * globalsc)742 bool EmitterScope::enterGlobal(BytecodeEmitter* bce,
743 GlobalSharedContext* globalsc) {
744 MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck());
745
746 bce->setVarEmitterScope(this);
747
748 if (!ensureCache(bce)) {
749 return false;
750 }
751
752 if (bce->emitterMode == BytecodeEmitter::SelfHosting) {
753 // In self-hosting, it is incorrect to consult the global scope because
754 // self-hosted scripts are cloned into their target compartments before
755 // they are run. Instead of Global, Intrinsic is used for all names.
756 //
757 // Intrinsic lookups are redirected to the special intrinsics holder
758 // in the global object, into which any missing values are cloned
759 // lazily upon first access.
760 fallbackFreeNameLocation_ = Some(NameLocation::Intrinsic());
761
762 return internEmptyGlobalScopeAsBody(bce);
763 }
764
765 auto createScope = [globalsc, bce](JSContext* cx,
766 Handle<AbstractScopePtr> enclosing,
767 ScopeIndex* index) {
768 MOZ_ASSERT(!enclosing.get());
769 return ScopeCreationData::create(cx, bce->compilationInfo,
770 globalsc->scopeKind(), globalsc->bindings,
771 index);
772 };
773 if (!internBodyScopeCreationData(bce, createScope)) {
774 return false;
775 }
776
777 // See: JSScript::outermostScope.
778 MOZ_ASSERT(bce->bodyScopeIndex == 0, "Global scope must be index 0");
779
780 // Resolve binding names and emit Def{Var,Let,Const} prologue ops.
781 if (globalsc->bindings) {
782 // Check for declaration conflicts before the Def* ops.
783 if (!bce->emit1(JSOp::CheckGlobalOrEvalDecl)) {
784 return false;
785 }
786
787 for (DynamicBindingIter bi(globalsc); bi; bi++) {
788 NameLocation loc = NameLocation::fromBinding(bi.kind(), bi.location());
789 JSAtom* name = bi.name();
790 if (!putNameInCache(bce, name, loc)) {
791 return false;
792 }
793
794 // Define the name in the prologue. Do not emit DefVar for
795 // functions that we'll emit DefFun for.
796 if (bi.isTopLevelFunction()) {
797 continue;
798 }
799
800 if (!bce->emitAtomOp(bi.bindingOp(), name)) {
801 return false;
802 }
803 }
804 }
805
806 // Note that to save space, we don't add free names to the cache for
807 // global scopes. They are assumed to be global vars in the syntactic
808 // global scope, dynamic accesses under non-syntactic global scope.
809 if (globalsc->scopeKind() == ScopeKind::Global) {
810 fallbackFreeNameLocation_ = Some(NameLocation::Global(BindingKind::Var));
811 } else {
812 fallbackFreeNameLocation_ = Some(NameLocation::Dynamic());
813 }
814
815 return true;
816 }
817
enterEval(BytecodeEmitter * bce,EvalSharedContext * evalsc)818 bool EmitterScope::enterEval(BytecodeEmitter* bce, EvalSharedContext* evalsc) {
819 MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck());
820
821 bce->setVarEmitterScope(this);
822
823 if (!ensureCache(bce)) {
824 return false;
825 }
826
827 // For simplicity, treat all free name lookups in eval scripts as dynamic.
828 fallbackFreeNameLocation_ = Some(NameLocation::Dynamic());
829
830 // Create the `var` scope. Note that there is also a lexical scope, created
831 // separately in emitScript().
832 auto createScope = [evalsc, bce](JSContext* cx,
833 Handle<AbstractScopePtr> enclosing,
834 ScopeIndex* index) {
835 ScopeKind scopeKind =
836 evalsc->strict() ? ScopeKind::StrictEval : ScopeKind::Eval;
837 return ScopeCreationData::create(cx, bce->compilationInfo, scopeKind,
838 evalsc->bindings, enclosing, index);
839 };
840 if (!internBodyScopeCreationData(bce, createScope)) {
841 return false;
842 }
843
844 if (hasEnvironment()) {
845 if (!bce->emitInternedScopeOp(index(), JSOp::PushVarEnv)) {
846 return false;
847 }
848 } else {
849 // Resolve binding names and emit DefVar prologue ops if we don't have
850 // an environment (i.e., a sloppy eval).
851 // Eval scripts always have their own lexical scope, but non-strict
852 // scopes may introduce 'var' bindings to the nearest var scope.
853 //
854 // TODO: We may optimize strict eval bindings in the future to be on
855 // the frame. For now, handle everything dynamically.
856 if (!hasEnvironment() && evalsc->bindings) {
857 // Check for declaration conflicts before the DefVar ops.
858 if (!bce->emit1(JSOp::CheckGlobalOrEvalDecl)) {
859 return false;
860 }
861
862 for (DynamicBindingIter bi(evalsc); bi; bi++) {
863 MOZ_ASSERT(bi.bindingOp() == JSOp::DefVar);
864
865 if (bi.isTopLevelFunction()) {
866 continue;
867 }
868
869 if (!bce->emitAtomOp(JSOp::DefVar, bi.name())) {
870 return false;
871 }
872 }
873 }
874
875 // As an optimization, if the eval does not have its own var
876 // environment and is directly enclosed in a global scope, then all
877 // free name lookups are global.
878 if (scope(bce).enclosing().is<GlobalScope>()) {
879 fallbackFreeNameLocation_ = Some(NameLocation::Global(BindingKind::Var));
880 }
881 }
882
883 return true;
884 }
885
enterModule(BytecodeEmitter * bce,ModuleSharedContext * modulesc)886 bool EmitterScope::enterModule(BytecodeEmitter* bce,
887 ModuleSharedContext* modulesc) {
888 MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck());
889
890 bce->setVarEmitterScope(this);
891
892 if (!ensureCache(bce)) {
893 return false;
894 }
895
896 // Resolve body-level bindings, if there are any.
897 TDZCheckCache* tdzCache = bce->innermostTDZCheckCache;
898 Maybe<uint32_t> firstLexicalFrameSlot;
899 if (ModuleScope::Data* bindings = modulesc->bindings) {
900 BindingIter bi(*bindings);
901 for (; bi; bi++) {
902 if (!checkSlotLimits(bce, bi)) {
903 return false;
904 }
905
906 NameLocation loc = NameLocation::fromBinding(bi.kind(), bi.location());
907 if (!putNameInCache(bce, bi.name(), loc)) {
908 return false;
909 }
910
911 if (BindingKindIsLexical(bi.kind())) {
912 if (loc.kind() == NameLocation::Kind::FrameSlot &&
913 !firstLexicalFrameSlot) {
914 firstLexicalFrameSlot = Some(loc.frameSlot());
915 }
916
917 if (!tdzCache->noteTDZCheck(bce, bi.name(), CheckTDZ)) {
918 return false;
919 }
920 }
921 }
922
923 updateFrameFixedSlots(bce, bi);
924 } else {
925 nextFrameSlot_ = 0;
926 }
927
928 // Modules are toplevel, so any free names are global.
929 fallbackFreeNameLocation_ = Some(NameLocation::Global(BindingKind::Var));
930
931 // Put lexical frame slots in TDZ. Environment slots are poisoned during
932 // environment creation.
933 if (firstLexicalFrameSlot) {
934 if (!deadZoneFrameSlotRange(bce, *firstLexicalFrameSlot, frameSlotEnd())) {
935 return false;
936 }
937 }
938
939 // Create and intern the VM scope creation data.
940 auto createScope = [modulesc, bce](JSContext* cx,
941 Handle<AbstractScopePtr> enclosing,
942 ScopeIndex* index) {
943 return ScopeCreationData::create(cx, bce->compilationInfo,
944 modulesc->bindings, modulesc->module(),
945 enclosing, index);
946 };
947 if (!internBodyScopeCreationData(bce, createScope)) {
948 return false;
949 }
950
951 return checkEnvironmentChainLength(bce);
952 }
953
enterWith(BytecodeEmitter * bce)954 bool EmitterScope::enterWith(BytecodeEmitter* bce) {
955 MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck());
956
957 if (!ensureCache(bce)) {
958 return false;
959 }
960
961 // 'with' make all accesses dynamic and unanalyzable.
962 fallbackFreeNameLocation_ = Some(NameLocation::Dynamic());
963
964 auto createScope = [bce](JSContext* cx, Handle<AbstractScopePtr> enclosing,
965 ScopeIndex* index) {
966 return ScopeCreationData::create(cx, bce->compilationInfo, enclosing,
967 index);
968 };
969 if (!internScopeCreationData(bce, createScope)) {
970 return false;
971 }
972
973 if (!bce->emitInternedScopeOp(index(), JSOp::EnterWith)) {
974 return false;
975 }
976
977 if (!appendScopeNote(bce)) {
978 return false;
979 }
980
981 return checkEnvironmentChainLength(bce);
982 }
983
deadZoneFrameSlots(BytecodeEmitter * bce) const984 bool EmitterScope::deadZoneFrameSlots(BytecodeEmitter* bce) const {
985 return deadZoneFrameSlotRange(bce, frameSlotStart(), frameSlotEnd());
986 }
987
leave(BytecodeEmitter * bce,bool nonLocal)988 bool EmitterScope::leave(BytecodeEmitter* bce, bool nonLocal) {
989 // If we aren't leaving the scope due to a non-local jump (e.g., break),
990 // we must be the innermost scope.
991 MOZ_ASSERT_IF(!nonLocal, this == bce->innermostEmitterScopeNoCheck());
992
993 ScopeKind kind = scope(bce).kind();
994 switch (kind) {
995 case ScopeKind::Lexical:
996 case ScopeKind::SimpleCatch:
997 case ScopeKind::Catch:
998 case ScopeKind::FunctionLexical:
999 if (!bce->emit1(hasEnvironment() ? JSOp::PopLexicalEnv
1000 : JSOp::DebugLeaveLexicalEnv)) {
1001 return false;
1002 }
1003 break;
1004
1005 case ScopeKind::With:
1006 if (!bce->emit1(JSOp::LeaveWith)) {
1007 return false;
1008 }
1009 break;
1010
1011 case ScopeKind::Function:
1012 case ScopeKind::FunctionBodyVar:
1013 case ScopeKind::NamedLambda:
1014 case ScopeKind::StrictNamedLambda:
1015 case ScopeKind::Eval:
1016 case ScopeKind::StrictEval:
1017 case ScopeKind::Global:
1018 case ScopeKind::NonSyntactic:
1019 case ScopeKind::Module:
1020 break;
1021
1022 case ScopeKind::WasmInstance:
1023 case ScopeKind::WasmFunction:
1024 MOZ_CRASH("No wasm function scopes in JS");
1025 }
1026
1027 // Finish up the scope if we are leaving it in LIFO fashion.
1028 if (!nonLocal) {
1029 // Popping scopes due to non-local jumps generate additional scope
1030 // notes. See NonLocalExitControl::prepareForNonLocalJump.
1031 if (ScopeKindIsInBody(kind)) {
1032 if (kind == ScopeKind::FunctionBodyVar) {
1033 // The extra function var scope is never popped once it's pushed,
1034 // so its scope note extends until the end of any possible code.
1035 bce->bytecodeSection().scopeNoteList().recordEndFunctionBodyVar(
1036 noteIndex_);
1037 } else {
1038 bce->bytecodeSection().scopeNoteList().recordEnd(
1039 noteIndex_, bce->bytecodeSection().offset());
1040 }
1041 }
1042 }
1043
1044 return true;
1045 }
1046
scope(const BytecodeEmitter * bce) const1047 AbstractScopePtr EmitterScope::scope(const BytecodeEmitter* bce) const {
1048 return bce->perScriptData().gcThingList().getScope(index());
1049 }
1050
lookup(BytecodeEmitter * bce,JSAtom * name)1051 NameLocation EmitterScope::lookup(BytecodeEmitter* bce, JSAtom* name) {
1052 if (Maybe<NameLocation> loc = lookupInCache(bce, name)) {
1053 return *loc;
1054 }
1055 return searchAndCache(bce, name);
1056 }
1057
locationBoundInScope(JSAtom * name,EmitterScope * target)1058 Maybe<NameLocation> EmitterScope::locationBoundInScope(JSAtom* name,
1059 EmitterScope* target) {
1060 // The target scope must be an intra-frame enclosing scope of this
1061 // one. Count the number of extra hops to reach it.
1062 uint8_t extraHops = 0;
1063 for (EmitterScope* es = this; es != target; es = es->enclosingInFrame()) {
1064 if (es->hasEnvironment()) {
1065 extraHops++;
1066 }
1067 }
1068
1069 // Caches are prepopulated with bound names. So if the name is bound in a
1070 // particular scope, it must already be in the cache. Furthermore, don't
1071 // consult the fallback location as we only care about binding names.
1072 Maybe<NameLocation> loc;
1073 if (NameLocationMap::Ptr p = target->nameCache_->lookup(name)) {
1074 NameLocation l = p->value().wrapped;
1075 if (l.kind() == NameLocation::Kind::EnvironmentCoordinate) {
1076 loc = Some(l.addHops(extraHops));
1077 } else {
1078 loc = Some(l);
1079 }
1080 }
1081 return loc;
1082 }
1083