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