blob: e2fa13c2966e7b90b33a99a77448e75350d0a344 [file] [log] [blame]
/* -*- 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,
SCOPE_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& scopeChain() const {
return getFixedSlot(SCOPE_CHAIN_SLOT).toObject();
}
void setScopeChain(JSObject& scopeChain) {
setFixedSlot(SCOPE_CHAIN_SLOT, ObjectValue(scopeChain));
}
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(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(SCOPE_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 offsetOfScopeChainSlot() {
return getFixedSlotOffset(SCOPE_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);
} // namespace js
template<>
inline bool
JSObject::is<js::GeneratorObject>() const
{
return is<js::LegacyGeneratorObject>() || is<js::StarGeneratorObject>();
}
#endif /* vm_GeneratorObject_h */