/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sts=4 et sw=4 tw=99: * 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_INDEX_RUNNING = INT32_MAX; static const int32_t YIELD_INDEX_CLOSING = INT32_MAX - 1; enum { CALLEE_SLOT = 0, ENV_CHAIN_SLOT, ARGS_OBJ_SLOT, EXPRESSION_STACK_SLOT, YIELD_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(); } 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_INDEX_RUNNING. If the generator is in that bizarre "closing" // state, the yield index is YIELD_INDEX_CLOSING. // // If the generator is suspended, it's the yield index (stored as // JSOP_INITIALYIELD/JSOP_YIELD 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_INDEX_SLOT).toInt32() == YIELD_INDEX_RUNNING; } bool isClosing() const { return getFixedSlot(YIELD_INDEX_SLOT).toInt32() == YIELD_INDEX_CLOSING; } bool isSuspended() const { // Note: also update Baseline's IsSuspendedStarGenerator code if this // changes. MOZ_ASSERT(!isClosed()); static_assert(YIELD_INDEX_CLOSING < YIELD_INDEX_RUNNING, "test below should return false for YIELD_INDEX_RUNNING"); return getFixedSlot(YIELD_INDEX_SLOT).toInt32() < YIELD_INDEX_CLOSING; } void setRunning() { MOZ_ASSERT(isSuspended()); setFixedSlot(YIELD_INDEX_SLOT, Int32Value(YIELD_INDEX_RUNNING)); } void setClosing() { MOZ_ASSERT(isSuspended()); setFixedSlot(YIELD_INDEX_SLOT, Int32Value(YIELD_INDEX_CLOSING)); } void setYieldIndex(uint32_t yieldIndex) { MOZ_ASSERT_IF(yieldIndex == 0, getFixedSlot(YIELD_INDEX_SLOT).isUndefined()); MOZ_ASSERT_IF(yieldIndex != 0, isRunning() || isClosing()); MOZ_ASSERT(yieldIndex < uint32_t(YIELD_INDEX_CLOSING)); setFixedSlot(YIELD_INDEX_SLOT, Int32Value(yieldIndex)); MOZ_ASSERT(isSuspended()); } uint32_t yieldIndex() const { MOZ_ASSERT(isSuspended()); return getFixedSlot(YIELD_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_INDEX_SLOT, NullValue()); setFixedSlot(NEWTARGET_SLOT, NullValue()); } 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 offsetOfYieldIndexSlot() { return getFixedSlotOffset(YIELD_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); MOZ_MUST_USE 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 */