GeneratorObject.h (8995B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 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 "builtin/SelfHostingDefines.h" 11 #include "js/Class.h" 12 #include "vm/ArgumentsObject.h" 13 #include "vm/ArrayObject.h" 14 #include "vm/BytecodeUtil.h" 15 #include "vm/GeneratorResumeKind.h" // GeneratorResumeKind 16 #include "vm/JSObject.h" 17 #include "vm/Stack.h" 18 19 namespace js { 20 21 class InterpreterActivation; 22 23 namespace frontend { 24 class TaggedParserAtomIndex; 25 } 26 27 extern const JSClass GeneratorFunctionClass; 28 29 class AbstractGeneratorObject : public NativeObject { 30 public: 31 // Magic value stored in the resumeIndex slot when the generator is 32 // running or closing. See the resumeIndex comment below. 33 static const int32_t RESUME_INDEX_RUNNING = INT32_MAX; 34 35 // Resume index of the initial yield. Used to identify the "suspended-start" 36 // generator state. 37 static const int32_t RESUME_INDEX_INITIAL_YIELD = 0; 38 39 enum { 40 CALLEE_SLOT = 0, 41 ENV_CHAIN_SLOT, 42 ARGS_OBJ_SLOT, 43 STACK_STORAGE_SLOT, 44 RESUME_INDEX_SLOT, 45 RESERVED_SLOTS 46 }; 47 48 static_assert(RESUME_INDEX_SLOT == GENERATOR_RESUME_INDEX_SLOT, 49 "RESUME_INDEX_SLOT must match self-hosting define for resume " 50 "index slot."); 51 52 static_assert(RESUME_INDEX_INITIAL_YIELD == 53 GENERATOR_RESUME_INDEX_INITIAL_YIELD, 54 "RESUME_INDEX_INITIAL_YIELD must match self-hosting define for " 55 "resume index initial yield."); 56 57 private: 58 static JSObject* createModuleGenerator(JSContext* cx, AbstractFramePtr frame); 59 60 public: 61 static JSObject* createFromFrame(JSContext* cx, AbstractFramePtr frame); 62 static AbstractGeneratorObject* create(JSContext* cx, HandleFunction callee, 63 HandleScript script, 64 HandleObject environmentChain, 65 Handle<ArgumentsObject*> argsObject); 66 67 static bool resume(JSContext* cx, InterpreterActivation& activation, 68 Handle<AbstractGeneratorObject*> genObj, HandleValue arg, 69 HandleValue resumeKind); 70 71 static bool suspend(JSContext* cx, HandleObject obj, AbstractFramePtr frame, 72 const jsbytecode* pc, unsigned nvalues); 73 74 static void finalSuspend(JSContext* cx, HandleObject obj); 75 76 JSFunction& callee() const { 77 return getFixedSlot(CALLEE_SLOT).toObject().as<JSFunction>(); 78 } 79 void setCallee(JSFunction& callee) { 80 setFixedSlot(CALLEE_SLOT, ObjectValue(callee)); 81 } 82 83 JSObject& environmentChain() const { 84 return getFixedSlot(ENV_CHAIN_SLOT).toObject(); 85 } 86 void setEnvironmentChain(JSObject& envChain) { 87 setFixedSlot(ENV_CHAIN_SLOT, ObjectValue(envChain)); 88 } 89 90 bool hasArgsObj() const { return getFixedSlot(ARGS_OBJ_SLOT).isObject(); } 91 ArgumentsObject& argsObj() const { 92 return getFixedSlot(ARGS_OBJ_SLOT).toObject().as<ArgumentsObject>(); 93 } 94 void setArgsObj(ArgumentsObject& argsObj) { 95 setFixedSlot(ARGS_OBJ_SLOT, ObjectValue(argsObj)); 96 } 97 98 bool hasStackStorage() const { 99 return getFixedSlot(STACK_STORAGE_SLOT).isObject(); 100 } 101 bool isStackStorageEmpty() const { 102 return stackStorage().getDenseInitializedLength() == 0; 103 } 104 ArrayObject& stackStorage() const { 105 return getFixedSlot(STACK_STORAGE_SLOT).toObject().as<ArrayObject>(); 106 } 107 void setStackStorage(ArrayObject& stackStorage) { 108 setFixedSlot(STACK_STORAGE_SLOT, ObjectValue(stackStorage)); 109 } 110 111 // Access stack storage. Requires `hasStackStorage() && isSuspended()`. 112 // `slot` is the index of the desired local in the stack frame when this 113 // generator is *not* suspended. 114 const Value& getUnaliasedLocal(uint32_t slot) const; 115 void setUnaliasedLocal(uint32_t slot, const Value& value); 116 117 // The resumeIndex slot is abused for a few purposes. It's undefined if 118 // it hasn't been set yet (before the initial yield), and null if the 119 // generator is closed. If the generator is running, the resumeIndex is 120 // RESUME_INDEX_RUNNING. 121 // 122 // If the generator is suspended, it's the resumeIndex (stored as 123 // JSOp::InitialYield/JSOp::Yield/JSOp::Await operand) of the yield 124 // instruction that suspended the generator. The resumeIndex can be mapped to 125 // the bytecode offset (interpreter) or to the native code offset (JIT). 126 127 bool isBeforeInitialYield() const { 128 return getFixedSlot(RESUME_INDEX_SLOT).isUndefined(); 129 } 130 bool isRunning() const { 131 return getFixedSlot(RESUME_INDEX_SLOT) == Int32Value(RESUME_INDEX_RUNNING); 132 } 133 bool isSuspended() const { 134 // Note: also update Baseline's IsSuspendedGenerator code if this 135 // changes. 136 Value resumeIndex = getFixedSlot(RESUME_INDEX_SLOT); 137 return resumeIndex.isInt32() && 138 resumeIndex.toInt32() < RESUME_INDEX_RUNNING; 139 } 140 void setRunning() { 141 MOZ_ASSERT(isSuspended()); 142 setFixedSlot(RESUME_INDEX_SLOT, Int32Value(RESUME_INDEX_RUNNING)); 143 } 144 void setResumeIndex(const jsbytecode* pc) { 145 MOZ_ASSERT(JSOp(*pc) == JSOp::InitialYield || JSOp(*pc) == JSOp::Yield || 146 JSOp(*pc) == JSOp::Await); 147 148 MOZ_ASSERT_IF(JSOp(*pc) == JSOp::InitialYield, 149 getFixedSlot(RESUME_INDEX_SLOT).isUndefined()); 150 MOZ_ASSERT_IF(JSOp(*pc) != JSOp::InitialYield, isRunning()); 151 152 uint32_t resumeIndex = GET_UINT24(pc); 153 MOZ_ASSERT(resumeIndex < uint32_t(RESUME_INDEX_RUNNING)); 154 155 setFixedSlot(RESUME_INDEX_SLOT, Int32Value(resumeIndex)); 156 MOZ_ASSERT(isSuspended()); 157 } 158 void setResumeIndex(int32_t resumeIndex) { 159 setFixedSlot(RESUME_INDEX_SLOT, Int32Value(resumeIndex)); 160 } 161 uint32_t resumeIndex() const { 162 MOZ_ASSERT(isSuspended()); 163 return getFixedSlot(RESUME_INDEX_SLOT).toInt32(); 164 } 165 bool isClosed() const { return getFixedSlot(CALLEE_SLOT).isNull(); } 166 void setClosed(JSContext* cx); 167 168 bool isAfterYield(); 169 bool isAfterAwait(); 170 171 private: 172 bool isAfterYieldOrAwait(JSOp op); 173 174 public: 175 void trace(JSTracer* trc); 176 177 static size_t offsetOfCalleeSlot() { return getFixedSlotOffset(CALLEE_SLOT); } 178 static size_t offsetOfEnvironmentChainSlot() { 179 return getFixedSlotOffset(ENV_CHAIN_SLOT); 180 } 181 static size_t offsetOfArgsObjSlot() { 182 return getFixedSlotOffset(ARGS_OBJ_SLOT); 183 } 184 static size_t offsetOfResumeIndexSlot() { 185 return getFixedSlotOffset(RESUME_INDEX_SLOT); 186 } 187 static size_t offsetOfStackStorageSlot() { 188 return getFixedSlotOffset(STACK_STORAGE_SLOT); 189 } 190 191 static size_t calleeSlot() { return CALLEE_SLOT; } 192 static size_t envChainSlot() { return ENV_CHAIN_SLOT; } 193 static size_t argsObjectSlot() { return ARGS_OBJ_SLOT; } 194 static size_t stackStorageSlot() { return STACK_STORAGE_SLOT; } 195 static size_t resumeIndexSlot() { return RESUME_INDEX_SLOT; } 196 197 #ifdef DEBUG 198 void dump() const; 199 #endif 200 }; 201 202 class GeneratorObject : public AbstractGeneratorObject { 203 public: 204 enum { RESERVED_SLOTS = AbstractGeneratorObject::RESERVED_SLOTS }; 205 206 static const JSClass class_; 207 static const JSClassOps classOps_; 208 209 static GeneratorObject* create(JSContext* cx, HandleFunction fun); 210 }; 211 212 bool GeneratorThrowOrReturn(JSContext* cx, AbstractFramePtr frame, 213 Handle<AbstractGeneratorObject*> obj, 214 HandleValue val, GeneratorResumeKind resumeKind); 215 216 /** 217 * Return the generator object associated with the given frame. The frame must 218 * be a call frame for a generator. 219 * 220 * This may return nullptr at certain points in the generator lifecycle: 221 * 222 * - While a generator call evaluates default argument values and performs 223 * destructuring, which occurs before the generator object is created. 224 * 225 * - Between the `Generator` instruction and the `SetAliasedVar .generator` 226 * instruction, at which point the generator object does exist, but is held 227 * only on the stack, and not the `.generator` pseudo-variable this function 228 * consults. 229 */ 230 AbstractGeneratorObject* GetGeneratorObjectForFrame(JSContext* cx, 231 AbstractFramePtr frame); 232 233 /** 234 * If `env` or any enclosing environment is a `CallObject` associated with a 235 * generator object, return the generator. 236 * 237 * Otherwise `env` is not in a generator or async function, or the generator 238 * object hasn't been created yet; return nullptr with no pending exception. 239 */ 240 AbstractGeneratorObject* GetGeneratorObjectForEnvironment(JSContext* cx, 241 HandleObject env); 242 243 GeneratorResumeKind ParserAtomToResumeKind( 244 frontend::TaggedParserAtomIndex atom); 245 JSAtom* ResumeKindToAtom(JSContext* cx, GeneratorResumeKind kind); 246 247 } // namespace js 248 249 template <> 250 bool JSObject::is<js::AbstractGeneratorObject>() const; 251 252 #endif /* vm_GeneratorObject_h */