tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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