summaryrefslogtreecommitdiffstats
path: root/js/src/frontend/ForOfLoopControl.h
blob: dec7737fd6e0807b18b8467446bdbc6925454dcb (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
/* -*- 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/. */

#ifndef frontend_ForOfLoopControl_h
#define frontend_ForOfLoopControl_h

#include "mozilla/Attributes.h"  // MOZ_MUST_USE
#include "mozilla/Maybe.h"       // mozilla::Maybe

#include <stdint.h>  // int32_t, uint32_t

#include "jsapi.h"  // CompletionKind

#include "frontend/BytecodeControlStructures.h"  // NestableControl, LoopControl
#include "frontend/BytecodeOffset.h"             // BytecodeOffset
#include "frontend/IteratorKind.h"               // IteratorKind
#include "frontend/TryEmitter.h"                 // TryEmitter

namespace js {
namespace frontend {

struct BytecodeEmitter;
class EmitterScope;

class ForOfLoopControl : public LoopControl {
  // The stack depth of the iterator.
  int32_t iterDepth_;

  // for-of loops, when throwing from non-iterator code (i.e. from the body
  // or from evaluating the LHS of the loop condition), need to call
  // IteratorClose.  This is done by enclosing non-iterator code with
  // try-catch and call IteratorClose in `catch` block.
  // If IteratorClose itself throws, we must not re-call IteratorClose. Since
  // non-local jumps like break and return call IteratorClose, whenever a
  // non-local jump is emitted, we must tell catch block not to perform
  // IteratorClose.
  //
  //   for (x of y) {
  //     // Operations for iterator (IteratorNext etc) are outside of
  //     // try-block.
  //     try {
  //       ...
  //       if (...) {
  //         // Before non-local jump, clear iterator on the stack to tell
  //         // catch block not to perform IteratorClose.
  //         tmpIterator = iterator;
  //         iterator = undefined;
  //         IteratorClose(tmpIterator, { break });
  //         break;
  //       }
  //       ...
  //     } catch (e) {
  //       // Just throw again when iterator is cleared by non-local jump.
  //       if (iterator === undefined)
  //         throw e;
  //       IteratorClose(iterator, { throw, e });
  //     }
  //   }
  mozilla::Maybe<TryEmitter> tryCatch_;

  // Used to track if any yields were emitted between calls to to
  // emitBeginCodeNeedingIteratorClose and emitEndCodeNeedingIteratorClose.
  uint32_t numYieldsAtBeginCodeNeedingIterClose_;

  bool allowSelfHosted_;

  IteratorKind iterKind_;

 public:
  ForOfLoopControl(BytecodeEmitter* bce, int32_t iterDepth,
                   bool allowSelfHosted, IteratorKind iterKind);

  MOZ_MUST_USE bool emitBeginCodeNeedingIteratorClose(BytecodeEmitter* bce);
  MOZ_MUST_USE bool emitEndCodeNeedingIteratorClose(BytecodeEmitter* bce);

  MOZ_MUST_USE bool emitIteratorCloseInInnermostScopeWithTryNote(
      BytecodeEmitter* bce,
      CompletionKind completionKind = CompletionKind::Normal);
  MOZ_MUST_USE bool emitIteratorCloseInScope(
      BytecodeEmitter* bce, EmitterScope& currentScope,
      CompletionKind completionKind = CompletionKind::Normal);

  MOZ_MUST_USE bool emitPrepareForNonLocalJumpFromScope(
      BytecodeEmitter* bce, EmitterScope& currentScope, bool isTarget,
      BytecodeOffset* tryNoteStart);
};
template <>
inline bool NestableControl::is<ForOfLoopControl>() const {
  return kind_ == StatementKind::ForOfLoop;
}

} /* namespace frontend */
} /* namespace js */

#endif /* frontend_ForOfLoopControl_h */