/src/mozilla-central/js/src/vm/GeneratorObject.h
Line | Count | Source (jump to first uncovered line) |
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 | | #ifndef vm_GeneratorObject_h |
8 | | #define vm_GeneratorObject_h |
9 | | |
10 | | #include "vm/ArgumentsObject.h" |
11 | | #include "vm/ArrayObject.h" |
12 | | #include "vm/JSContext.h" |
13 | | #include "vm/JSObject.h" |
14 | | #include "vm/Stack.h" |
15 | | |
16 | | namespace js { |
17 | | |
18 | | class GeneratorObject : public NativeObject |
19 | | { |
20 | | public: |
21 | | // Magic values stored in the yield index slot when the generator is |
22 | | // running or closing. See the yield index comment below. |
23 | | static const int32_t YIELD_AND_AWAIT_INDEX_RUNNING = INT32_MAX; |
24 | | static const int32_t YIELD_AND_AWAIT_INDEX_CLOSING = INT32_MAX - 1; |
25 | | |
26 | | enum { |
27 | | CALLEE_SLOT = 0, |
28 | | ENV_CHAIN_SLOT, |
29 | | ARGS_OBJ_SLOT, |
30 | | EXPRESSION_STACK_SLOT, |
31 | | YIELD_AND_AWAIT_INDEX_SLOT, |
32 | | RESERVED_SLOTS |
33 | | }; |
34 | | |
35 | | enum ResumeKind { NEXT, THROW, RETURN }; |
36 | | |
37 | | static const Class class_; |
38 | | |
39 | | private: |
40 | | static bool suspend(JSContext* cx, HandleObject obj, AbstractFramePtr frame, jsbytecode* pc, |
41 | | Value* vp, unsigned nvalues); |
42 | | |
43 | | public: |
44 | 0 | static inline ResumeKind getResumeKind(jsbytecode* pc) { |
45 | 0 | MOZ_ASSERT(*pc == JSOP_RESUME); |
46 | 0 | unsigned arg = GET_UINT16(pc); |
47 | 0 | MOZ_ASSERT(arg <= RETURN); |
48 | 0 | return static_cast<ResumeKind>(arg); |
49 | 0 | } |
50 | | |
51 | 18 | static inline ResumeKind getResumeKind(JSContext* cx, JSAtom* atom) { |
52 | 18 | if (atom == cx->names().next) { |
53 | 6 | return NEXT; |
54 | 6 | } |
55 | 12 | if (atom == cx->names().throw_) { |
56 | 6 | return THROW; |
57 | 6 | } |
58 | 6 | MOZ_ASSERT(atom == cx->names().return_); |
59 | 6 | return RETURN; |
60 | 6 | } |
61 | | |
62 | | static JSObject* create(JSContext* cx, AbstractFramePtr frame); |
63 | | |
64 | | static bool resume(JSContext* cx, InterpreterActivation& activation, |
65 | | Handle<GeneratorObject*> genObj, HandleValue arg); |
66 | | |
67 | 0 | static bool initialSuspend(JSContext* cx, HandleObject obj, AbstractFramePtr frame, jsbytecode* pc) { |
68 | 0 | return suspend(cx, obj, frame, pc, nullptr, 0); |
69 | 0 | } |
70 | | |
71 | | static bool normalSuspend(JSContext* cx, HandleObject obj, AbstractFramePtr frame, jsbytecode* pc, |
72 | 0 | Value* vp, unsigned nvalues) { |
73 | 0 | return suspend(cx, obj, frame, pc, vp, nvalues); |
74 | 0 | } |
75 | | |
76 | | static void finalSuspend(HandleObject obj); |
77 | | |
78 | 0 | JSFunction& callee() const { |
79 | 0 | return getFixedSlot(CALLEE_SLOT).toObject().as<JSFunction>(); |
80 | 0 | } |
81 | 0 | void setCallee(JSFunction& callee) { |
82 | 0 | setFixedSlot(CALLEE_SLOT, ObjectValue(callee)); |
83 | 0 | } |
84 | | |
85 | 0 | JSObject& environmentChain() const { |
86 | 0 | return getFixedSlot(ENV_CHAIN_SLOT).toObject(); |
87 | 0 | } |
88 | 0 | void setEnvironmentChain(JSObject& envChain) { |
89 | 0 | setFixedSlot(ENV_CHAIN_SLOT, ObjectValue(envChain)); |
90 | 0 | } |
91 | | |
92 | 0 | bool hasArgsObj() const { |
93 | 0 | return getFixedSlot(ARGS_OBJ_SLOT).isObject(); |
94 | 0 | } |
95 | 0 | ArgumentsObject& argsObj() const { |
96 | 0 | return getFixedSlot(ARGS_OBJ_SLOT).toObject().as<ArgumentsObject>(); |
97 | 0 | } |
98 | 0 | void setArgsObj(ArgumentsObject& argsObj) { |
99 | 0 | setFixedSlot(ARGS_OBJ_SLOT, ObjectValue(argsObj)); |
100 | 0 | } |
101 | | |
102 | 0 | bool hasExpressionStack() const { |
103 | 0 | return getFixedSlot(EXPRESSION_STACK_SLOT).isObject(); |
104 | 0 | } |
105 | 0 | bool isExpressionStackEmpty() const { |
106 | 0 | return expressionStack().getDenseInitializedLength() == 0; |
107 | 0 | } |
108 | 0 | ArrayObject& expressionStack() const { |
109 | 0 | return getFixedSlot(EXPRESSION_STACK_SLOT).toObject().as<ArrayObject>(); |
110 | 0 | } |
111 | 0 | void setExpressionStack(ArrayObject& expressionStack) { |
112 | 0 | setFixedSlot(EXPRESSION_STACK_SLOT, ObjectValue(expressionStack)); |
113 | 0 | } |
114 | 0 | void clearExpressionStack() { |
115 | 0 | setFixedSlot(EXPRESSION_STACK_SLOT, NullValue()); |
116 | 0 | } |
117 | | |
118 | | // The yield index slot is abused for a few purposes. It's undefined if |
119 | | // it hasn't been set yet (before the initial yield), and null if the |
120 | | // generator is closed. If the generator is running, the yield index is |
121 | | // YIELD_AND_AWAIT_INDEX_RUNNING. If the generator is in that bizarre |
122 | | // "closing" state, the yield index is YIELD_AND_AWAIT_INDEX_CLOSING. |
123 | | // |
124 | | // If the generator is suspended, it's the yield index (stored as |
125 | | // JSOP_INITIALYIELD/JSOP_YIELD/JSOP_AWAIT operand) of the yield |
126 | | // instruction that suspended the generator. The yield index can be mapped |
127 | | // to the bytecode offset (interpreter) or to the native code offset (JIT). |
128 | | |
129 | 0 | bool isBeforeInitialYield() const { |
130 | 0 | return getFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT).isUndefined(); |
131 | 0 | } |
132 | 0 | bool isRunning() const { |
133 | 0 | MOZ_ASSERT(!isClosed()); |
134 | 0 | return getFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT).toInt32() == YIELD_AND_AWAIT_INDEX_RUNNING; |
135 | 0 | } |
136 | 0 | bool isClosing() const { |
137 | 0 | return getFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT).toInt32() == YIELD_AND_AWAIT_INDEX_CLOSING; |
138 | 0 | } |
139 | 0 | bool isSuspended() const { |
140 | 0 | // Note: also update Baseline's IsSuspendedGenerator code if this |
141 | 0 | // changes. |
142 | 0 | MOZ_ASSERT(!isClosed()); |
143 | 0 | static_assert(YIELD_AND_AWAIT_INDEX_CLOSING < YIELD_AND_AWAIT_INDEX_RUNNING, |
144 | 0 | "test below should return false for YIELD_AND_AWAIT_INDEX_RUNNING"); |
145 | 0 | return getFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT).toInt32() < YIELD_AND_AWAIT_INDEX_CLOSING; |
146 | 0 | } |
147 | 0 | void setRunning() { |
148 | 0 | MOZ_ASSERT(isSuspended()); |
149 | 0 | setFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT, Int32Value(YIELD_AND_AWAIT_INDEX_RUNNING)); |
150 | 0 | } |
151 | 0 | void setClosing() { |
152 | 0 | MOZ_ASSERT(isRunning()); |
153 | 0 | setFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT, Int32Value(YIELD_AND_AWAIT_INDEX_CLOSING)); |
154 | 0 | } |
155 | 0 | void setYieldAndAwaitIndex(uint32_t yieldAndAwaitIndex) { |
156 | 0 | MOZ_ASSERT_IF(yieldAndAwaitIndex == 0, |
157 | 0 | getFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT).isUndefined()); |
158 | 0 | MOZ_ASSERT_IF(yieldAndAwaitIndex != 0, isRunning() || isClosing()); |
159 | 0 | setYieldAndAwaitIndexNoAssert(yieldAndAwaitIndex); |
160 | 0 | } |
161 | | // Debugger has to flout the state machine rules a bit. |
162 | 0 | void setYieldAndAwaitIndexNoAssert(uint32_t yieldAndAwaitIndex) { |
163 | 0 | MOZ_ASSERT(yieldAndAwaitIndex < uint32_t(YIELD_AND_AWAIT_INDEX_CLOSING)); |
164 | 0 | setFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT, Int32Value(yieldAndAwaitIndex)); |
165 | 0 | MOZ_ASSERT(isSuspended()); |
166 | 0 | } |
167 | 0 | uint32_t yieldAndAwaitIndex() const { |
168 | 0 | MOZ_ASSERT(isSuspended()); |
169 | 0 | return getFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT).toInt32(); |
170 | 0 | } |
171 | 0 | bool isClosed() const { |
172 | 0 | return getFixedSlot(CALLEE_SLOT).isNull(); |
173 | 0 | } |
174 | 0 | void setClosed() { |
175 | 0 | setFixedSlot(CALLEE_SLOT, NullValue()); |
176 | 0 | setFixedSlot(ENV_CHAIN_SLOT, NullValue()); |
177 | 0 | setFixedSlot(ARGS_OBJ_SLOT, NullValue()); |
178 | 0 | setFixedSlot(EXPRESSION_STACK_SLOT, NullValue()); |
179 | 0 | setFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT, NullValue()); |
180 | 0 | } |
181 | | |
182 | | bool isAfterYield(); |
183 | | bool isAfterAwait(); |
184 | | |
185 | | private: |
186 | | bool isAfterYieldOrAwait(JSOp op); |
187 | | |
188 | | public: |
189 | 0 | static size_t offsetOfCalleeSlot() { |
190 | 0 | return getFixedSlotOffset(CALLEE_SLOT); |
191 | 0 | } |
192 | 0 | static size_t offsetOfEnvironmentChainSlot() { |
193 | 0 | return getFixedSlotOffset(ENV_CHAIN_SLOT); |
194 | 0 | } |
195 | 0 | static size_t offsetOfArgsObjSlot() { |
196 | 0 | return getFixedSlotOffset(ARGS_OBJ_SLOT); |
197 | 0 | } |
198 | 0 | static size_t offsetOfYieldAndAwaitIndexSlot() { |
199 | 0 | return getFixedSlotOffset(YIELD_AND_AWAIT_INDEX_SLOT); |
200 | 0 | } |
201 | 0 | static size_t offsetOfExpressionStackSlot() { |
202 | 0 | return getFixedSlotOffset(EXPRESSION_STACK_SLOT); |
203 | 0 | } |
204 | | }; |
205 | | |
206 | | bool GeneratorThrowOrReturn(JSContext* cx, AbstractFramePtr frame, Handle<GeneratorObject*> obj, |
207 | | HandleValue val, uint32_t resumeKind); |
208 | | |
209 | | /** |
210 | | * Return the generator object associated with the given frame. The frame must |
211 | | * be a call frame for a generator. If the generator object hasn't been created |
212 | | * yet, or hasn't been stored in the stack slot yet, this returns null. |
213 | | */ |
214 | | GeneratorObject* |
215 | | GetGeneratorObjectForFrame(JSContext* cx, AbstractFramePtr frame); |
216 | | |
217 | | void SetGeneratorClosed(JSContext* cx, AbstractFramePtr frame); |
218 | | |
219 | | } // namespace js |
220 | | |
221 | | #endif /* vm_GeneratorObject_h */ |