summaryrefslogtreecommitdiff
path: root/js/src/vm/GeneratorObject.h
blob: d5d7e7dbc2a6955176fb88e2157c341bc1761df3 (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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * 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 vm_GeneratorObject_h
#define vm_GeneratorObject_h

#include "jscntxt.h"
#include "jsobj.h"

#include "vm/ArgumentsObject.h"
#include "vm/ArrayObject.h"
#include "vm/Stack.h"

namespace js {

class GeneratorObject : public NativeObject
{
  public:
    // Magic values stored in the yield index slot when the generator is
    // running or closing. See the yield index comment below.
    static const int32_t YIELD_AND_AWAIT_INDEX_RUNNING = INT32_MAX;
    static const int32_t YIELD_AND_AWAIT_INDEX_CLOSING = INT32_MAX - 1;

    enum {
        CALLEE_SLOT = 0,
        ENV_CHAIN_SLOT,
        ARGS_OBJ_SLOT,
        EXPRESSION_STACK_SLOT,
        YIELD_AND_AWAIT_INDEX_SLOT,
        NEWTARGET_SLOT,
        RESERVED_SLOTS
    };

    enum ResumeKind { NEXT, THROW, CLOSE };

  private:
    static bool suspend(JSContext* cx, HandleObject obj, AbstractFramePtr frame, jsbytecode* pc,
                        Value* vp, unsigned nvalues);

  public:
    static inline ResumeKind getResumeKind(jsbytecode* pc) {
        MOZ_ASSERT(*pc == JSOP_RESUME);
        unsigned arg = GET_UINT16(pc);
        MOZ_ASSERT(arg <= CLOSE);
        return static_cast<ResumeKind>(arg);
    }

    static inline ResumeKind getResumeKind(ExclusiveContext* cx, JSAtom* atom) {
        if (atom == cx->names().next)
            return NEXT;
        if (atom == cx->names().throw_)
            return THROW;
        MOZ_ASSERT(atom == cx->names().close);
        return CLOSE;
    }

    static JSObject* create(JSContext* cx, AbstractFramePtr frame);

    static bool resume(JSContext* cx, InterpreterActivation& activation,
                       HandleObject obj, HandleValue arg, ResumeKind resumeKind);

    static bool initialSuspend(JSContext* cx, HandleObject obj, AbstractFramePtr frame, jsbytecode* pc) {
        return suspend(cx, obj, frame, pc, nullptr, 0);
    }

    static bool normalSuspend(JSContext* cx, HandleObject obj, AbstractFramePtr frame, jsbytecode* pc,
                              Value* vp, unsigned nvalues) {
        return suspend(cx, obj, frame, pc, vp, nvalues);
    }

    static bool finalSuspend(JSContext* cx, HandleObject obj);

    JSFunction& callee() const {
        return getFixedSlot(CALLEE_SLOT).toObject().as<JSFunction>();
    }
    void setCallee(JSFunction& callee) {
        setFixedSlot(CALLEE_SLOT, ObjectValue(callee));
    }

    JSObject& environmentChain() const {
        return getFixedSlot(ENV_CHAIN_SLOT).toObject();
    }
    void setEnvironmentChain(JSObject& envChain) {
        setFixedSlot(ENV_CHAIN_SLOT, ObjectValue(envChain));
    }

    bool hasArgsObj() const {
        return getFixedSlot(ARGS_OBJ_SLOT).isObject();
    }
    ArgumentsObject& argsObj() const {
        return getFixedSlot(ARGS_OBJ_SLOT).toObject().as<ArgumentsObject>();
    }
    void setArgsObj(ArgumentsObject& argsObj) {
        setFixedSlot(ARGS_OBJ_SLOT, ObjectValue(argsObj));
    }

    bool hasExpressionStack() const {
        return getFixedSlot(EXPRESSION_STACK_SLOT).isObject();
    }
    bool isExpressionStackEmpty() const {
        return expressionStack().getDenseInitializedLength() == 0;
    }
    ArrayObject& expressionStack() const {
        return getFixedSlot(EXPRESSION_STACK_SLOT).toObject().as<ArrayObject>();
    }
    void setExpressionStack(ArrayObject& expressionStack) {
        setFixedSlot(EXPRESSION_STACK_SLOT, ObjectValue(expressionStack));
    }
    void clearExpressionStack() {
        setFixedSlot(EXPRESSION_STACK_SLOT, NullValue());
    }

    bool isConstructing() const {
        return getFixedSlot(NEWTARGET_SLOT).isObject();
    }
    const Value& newTarget() const {
        return getFixedSlot(NEWTARGET_SLOT);
    }
    void setNewTarget(const Value& newTarget) {
        setFixedSlot(NEWTARGET_SLOT, newTarget);
    }


    // The yield index slot is abused for a few purposes.  It's undefined if
    // it hasn't been set yet (before the initial yield), and null if the
    // generator is closed. If the generator is running, the yield index is
    // YIELD_AND_AWAIT_INDEX_RUNNING. If the generator is in that bizarre
    // "closing" state, the yield index is YIELD_AND_AWAIT_INDEX_CLOSING.
    //
    // If the generator is suspended, it's the yield index (stored as
    // JSOP_INITIALYIELD/JSOP_YIELD/JSOP_AWAIT operand) of the yield
    // instruction that suspended the generator. The yield index can be mapped
    // to the bytecode offset (interpreter) or to the native code offset (JIT).

    bool isRunning() const {
        MOZ_ASSERT(!isClosed());
        return getFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT).toInt32() == YIELD_AND_AWAIT_INDEX_RUNNING;
    }
    bool isClosing() const {
        return getFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT).toInt32() == YIELD_AND_AWAIT_INDEX_CLOSING;
    }
    bool isSuspended() const {
        // Note: also update Baseline's IsSuspendedStarGenerator code if this
        // changes.
        MOZ_ASSERT(!isClosed());
        static_assert(YIELD_AND_AWAIT_INDEX_CLOSING < YIELD_AND_AWAIT_INDEX_RUNNING,
                      "test below should return false for YIELD_AND_AWAIT_INDEX_RUNNING");
        return getFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT).toInt32() < YIELD_AND_AWAIT_INDEX_CLOSING;
    }
    void setRunning() {
        MOZ_ASSERT(isSuspended());
        setFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT, Int32Value(YIELD_AND_AWAIT_INDEX_RUNNING));
    }
    void setClosing() {
        MOZ_ASSERT(isSuspended());
        setFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT, Int32Value(YIELD_AND_AWAIT_INDEX_CLOSING));
    }
    void setYieldAndAwaitIndex(uint32_t yieldAndAwaitIndex) {
        MOZ_ASSERT_IF(yieldAndAwaitIndex == 0,
                      getFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT).isUndefined());
        MOZ_ASSERT_IF(yieldAndAwaitIndex != 0, isRunning() || isClosing());
        MOZ_ASSERT(yieldAndAwaitIndex < uint32_t(YIELD_AND_AWAIT_INDEX_CLOSING));
        setFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT, Int32Value(yieldAndAwaitIndex));
        MOZ_ASSERT(isSuspended());
    }
    uint32_t yieldAndAwaitIndex() const {
        MOZ_ASSERT(isSuspended());
        return getFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT).toInt32();
    }
    bool isClosed() const {
        return getFixedSlot(CALLEE_SLOT).isNull();
    }
    void setClosed() {
        setFixedSlot(CALLEE_SLOT, NullValue());
        setFixedSlot(ENV_CHAIN_SLOT, NullValue());
        setFixedSlot(ARGS_OBJ_SLOT, NullValue());
        setFixedSlot(EXPRESSION_STACK_SLOT, NullValue());
        setFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT, NullValue());
        setFixedSlot(NEWTARGET_SLOT, NullValue());
    }

    bool isAfterYield();
    bool isAfterAwait();

private:
    bool isAfterYieldOrAwait(JSOp op);

public:
    static size_t offsetOfCalleeSlot() {
        return getFixedSlotOffset(CALLEE_SLOT);
    }
    static size_t offsetOfEnvironmentChainSlot() {
        return getFixedSlotOffset(ENV_CHAIN_SLOT);
    }
    static size_t offsetOfArgsObjSlot() {
        return getFixedSlotOffset(ARGS_OBJ_SLOT);
    }
    static size_t offsetOfYieldAndAwaitIndexSlot() {
        return getFixedSlotOffset(YIELD_AND_AWAIT_INDEX_SLOT);
    }
    static size_t offsetOfExpressionStackSlot() {
        return getFixedSlotOffset(EXPRESSION_STACK_SLOT);
    }
    static size_t offsetOfNewTargetSlot() {
        return getFixedSlotOffset(NEWTARGET_SLOT);
    }
};

class LegacyGeneratorObject : public GeneratorObject
{
  public:
    static const Class class_;

    static bool close(JSContext* cx, HandleObject obj);
};

class StarGeneratorObject : public GeneratorObject
{
  public:
    static const Class class_;
};

bool GeneratorThrowOrClose(JSContext* cx, AbstractFramePtr frame, Handle<GeneratorObject*> obj,
                           HandleValue val, uint32_t resumeKind);
void SetReturnValueForClosingGenerator(JSContext* cx, AbstractFramePtr frame);

[[nodiscard]] bool
CheckStarGeneratorResumptionValue(JSContext* cx, HandleValue v);

} // namespace js

template<>
inline bool
JSObject::is<js::GeneratorObject>() const
{
    return is<js::LegacyGeneratorObject>() || is<js::StarGeneratorObject>();
}

#endif /* vm_GeneratorObject_h */