Coverage Report

Created: 2018-09-25 14:53

/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 */