summaryrefslogtreecommitdiffstats
path: root/js/src/frontend/CForEmitter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/frontend/CForEmitter.cpp')
-rw-r--r--js/src/frontend/CForEmitter.cpp179
1 files changed, 179 insertions, 0 deletions
diff --git a/js/src/frontend/CForEmitter.cpp b/js/src/frontend/CForEmitter.cpp
new file mode 100644
index 0000000000..4fccde0e88
--- /dev/null
+++ b/js/src/frontend/CForEmitter.cpp
@@ -0,0 +1,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;
+}