summaryrefslogtreecommitdiffstats
path: root/js/src/frontend/CForEmitter.cpp
blob: 4fccde0e88476d7132f74205797faf0a9ca4890d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "frontend/CForEmitter.h"

#include "frontend/BytecodeEmitter.h"  // BytecodeEmitter
#include "frontend/EmitterScope.h"     // EmitterScope
#include "vm/Opcodes.h"                // JSOp
#include "vm/ScopeKind.h"              // ScopeKind
#include "vm/StencilEnums.h"           // TryNoteKind

using namespace js;
using namespace js::frontend;

using mozilla::Maybe;

CForEmitter::CForEmitter(BytecodeEmitter* bce,
                         const EmitterScope* headLexicalEmitterScopeForLet)
    : bce_(bce),
      headLexicalEmitterScopeForLet_(headLexicalEmitterScopeForLet) {}

bool CForEmitter::emitInit(const Maybe<uint32_t>& initPos) {
  MOZ_ASSERT(state_ == State::Start);

  loopInfo_.emplace(bce_, StatementKind::ForLoop);

  if (initPos) {
    if (!bce_->updateSourceCoordNotes(*initPos)) {
      return false;
    }
  }

#ifdef DEBUG
  state_ = State::Init;
#endif
  return true;
}

bool CForEmitter::emitCond(const Maybe<uint32_t>& condPos) {
  MOZ_ASSERT(state_ == State::Init);

  // ES 13.7.4.8 step 2. The initial freshening.
  //
  // If an initializer let-declaration may be captured during loop
  // iteration, the current scope has an environment.  If so, freshen the
  // current environment to expose distinct bindings for each loop
  // iteration.
  if (headLexicalEmitterScopeForLet_) {
    // The environment chain only includes an environment for the
    // for(;;) loop head's let-declaration *if* a scope binding is
    // captured, thus requiring a fresh environment each iteration. If
    // a lexical scope exists for the head, it must be the innermost
    // one. If that scope has closed-over bindings inducing an
    // environment, recreate the current environment.
    MOZ_ASSERT(headLexicalEmitterScopeForLet_ == bce_->innermostEmitterScope());
    MOZ_ASSERT(headLexicalEmitterScopeForLet_->scope(bce_).kind() ==
               ScopeKind::Lexical);

    if (headLexicalEmitterScopeForLet_->hasEnvironment()) {
      if (!bce_->emitInternedScopeOp(headLexicalEmitterScopeForLet_->index(),
                                     JSOp::FreshenLexicalEnv)) {
        return false;
      }
    }
  }

  if (!loopInfo_->emitLoopHead(bce_, condPos)) {
    //              [stack]
    return false;
  }

#ifdef DEBUG
  state_ = State::Cond;
#endif
  return true;
}

bool CForEmitter::emitBody(Cond cond) {
  MOZ_ASSERT(state_ == State::Cond);
  cond_ = cond;

  if (cond_ == Cond::Present) {
    if (!bce_->emitJump(JSOp::JumpIfFalse, &loopInfo_->breaks)) {
      return false;
    }
  }

  tdzCache_.emplace(bce_);

#ifdef DEBUG
  state_ = State::Body;
#endif
  return true;
}

bool CForEmitter::emitUpdate(Update update, const Maybe<uint32_t>& updatePos) {
  MOZ_ASSERT(state_ == State::Body);
  update_ = update;
  tdzCache_.reset();

  // Set loop and enclosing "update" offsets, for continue.  Note that we
  // continue to immediately *before* the block-freshening: continuing must
  // refresh the block.
  if (!loopInfo_->emitContinueTarget(bce_)) {
    return false;
  }

  // ES 13.7.4.8 step 3.e. The per-iteration freshening.
  if (headLexicalEmitterScopeForLet_) {
    MOZ_ASSERT(headLexicalEmitterScopeForLet_ == bce_->innermostEmitterScope());
    MOZ_ASSERT(headLexicalEmitterScopeForLet_->scope(bce_).kind() ==
               ScopeKind::Lexical);

    if (headLexicalEmitterScopeForLet_->hasEnvironment()) {
      if (!bce_->emitInternedScopeOp(headLexicalEmitterScopeForLet_->index(),
                                     JSOp::FreshenLexicalEnv)) {
        return false;
      }
    }
  }

  // The update code may not be executed at all; it needs its own TDZ
  // cache.
  if (update_ == Update::Present) {
    tdzCache_.emplace(bce_);

    if (updatePos) {
      if (!bce_->updateSourceCoordNotes(*updatePos)) {
        return false;
      }
    }
  }

#ifdef DEBUG
  state_ = State::Update;
#endif
  return true;
}

bool CForEmitter::emitEnd(uint32_t forPos) {
  MOZ_ASSERT(state_ == State::Update);

  if (update_ == Update::Present) {
    tdzCache_.reset();

    //              [stack] UPDATE

    if (!bce_->emit1(JSOp::Pop)) {
      //            [stack]
      return false;
    }
  }

  if (cond_ == Cond::Missing && update_ == Update::Missing) {
    // If there is no condition clause and no update clause, mark
    // the loop-ending "goto" with the location of the "for".
    // This ensures that the debugger will stop on each loop
    // iteration.
    if (!bce_->updateSourceCoordNotes(forPos)) {
      return false;
    }
  }

  // Emit the loop-closing jump.
  if (!loopInfo_->emitLoopEnd(bce_, JSOp::Goto, TryNoteKind::Loop)) {
    //              [stack]
    return false;
  }

  loopInfo_.reset();

#ifdef DEBUG
  state_ = State::End;
#endif
  return true;
}