Source code
Revision control
Copy as Markdown
Other Tools
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* 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
#include "frontend/ObjectEmitter.h"
#include "mozilla/Assertions.h" // MOZ_ASSERT
#include "frontend/BytecodeEmitter.h" // BytecodeEmitter
#include "frontend/IfEmitter.h" // IfEmitter
#include "frontend/ParseNode.h" // AccessorType
#include "frontend/SharedContext.h" // SharedContext
#include "vm/FunctionPrefixKind.h" // FunctionPrefixKind
#include "vm/Opcodes.h" // JSOp
using namespace js;
using namespace js::frontend;
PropertyEmitter::PropertyEmitter(BytecodeEmitter* bce) : bce_(bce) {}
bool PropertyEmitter::prepareForProtoValue(uint32_t keyPos) {
MOZ_ASSERT(propertyState_ == PropertyState::Start ||
propertyState_ == PropertyState::Init);
// [stack] CTOR? OBJ CTOR?
if (!bce_->updateSourceCoordNotes(keyPos)) {
return false;
}
#ifdef DEBUG
propertyState_ = PropertyState::ProtoValue;
#endif
return true;
}
bool PropertyEmitter::emitMutateProto() {
MOZ_ASSERT(propertyState_ == PropertyState::ProtoValue);
// [stack] OBJ PROTO
if (!bce_->emit1(JSOp::MutateProto)) {
// [stack] OBJ
return false;
}
#ifdef DEBUG
propertyState_ = PropertyState::Init;
#endif
return true;
}
bool PropertyEmitter::prepareForSpreadOperand(uint32_t spreadPos) {
MOZ_ASSERT(propertyState_ == PropertyState::Start ||
propertyState_ == PropertyState::Init);
// [stack] OBJ
if (!bce_->updateSourceCoordNotes(spreadPos)) {
return false;
}
if (!bce_->emit1(JSOp::Dup)) {
// [stack] OBJ OBJ
return false;
}
#ifdef DEBUG
propertyState_ = PropertyState::SpreadOperand;
#endif
return true;
}
bool PropertyEmitter::emitSpread() {
MOZ_ASSERT(propertyState_ == PropertyState::SpreadOperand);
// [stack] OBJ OBJ VAL
if (!bce_->emitCopyDataProperties(BytecodeEmitter::CopyOption::Unfiltered)) {
// [stack] OBJ
return false;
}
#ifdef DEBUG
propertyState_ = PropertyState::Init;
#endif
return true;
}
MOZ_ALWAYS_INLINE bool PropertyEmitter::prepareForProp(uint32_t keyPos,
bool isStatic,
bool isIndexOrComputed) {
isStatic_ = isStatic;
isIndexOrComputed_ = isIndexOrComputed;
// [stack] CTOR? OBJ
if (!bce_->updateSourceCoordNotes(keyPos)) {
return false;
}
if (isStatic_) {
if (!bce_->emit1(JSOp::Dup2)) {
// [stack] CTOR HOMEOBJ CTOR HOMEOBJ
return false;
}
if (!bce_->emit1(JSOp::Pop)) {
// [stack] CTOR HOMEOBJ CTOR
return false;
}
}
return true;
}
bool PropertyEmitter::prepareForPrivateMethod() {
MOZ_ASSERT(propertyState_ == PropertyState::Start ||
propertyState_ == PropertyState::Init);
MOZ_ASSERT(isClass_);
isStatic_ = false;
isIndexOrComputed_ = false;
#ifdef DEBUG
propertyState_ = PropertyState::PrivateMethodValue;
#endif
return true;
}
bool PropertyEmitter::prepareForPrivateStaticMethod(uint32_t keyPos) {
MOZ_ASSERT(propertyState_ == PropertyState::Start ||
propertyState_ == PropertyState::Init);
MOZ_ASSERT(isClass_);
// [stack] CTOR OBJ
if (!prepareForProp(keyPos,
/* isStatic_ = */ true,
/* isIndexOrComputed = */ true)) {
// [stack] CTOR OBJ CTOR
return false;
}
#ifdef DEBUG
propertyState_ = PropertyState::PrivateStaticMethod;
#endif
return true;
}
bool PropertyEmitter::prepareForPropValue(uint32_t keyPos, Kind kind) {
MOZ_ASSERT(propertyState_ == PropertyState::Start ||
propertyState_ == PropertyState::Init);
// [stack] CTOR? OBJ
if (!prepareForProp(keyPos,
/* isStatic_ = */ kind == Kind::Static,
/* isIndexOrComputed = */ false)) {
// [stack] CTOR? OBJ CTOR?
return false;
}
#ifdef DEBUG
propertyState_ = PropertyState::PropValue;
#endif
return true;
}
bool PropertyEmitter::prepareForIndexPropKey(uint32_t keyPos, Kind kind) {
MOZ_ASSERT(propertyState_ == PropertyState::Start ||
propertyState_ == PropertyState::Init);
// [stack] CTOR? OBJ
if (!prepareForProp(keyPos,
/* isStatic_ = */ kind == Kind::Static,
/* isIndexOrComputed = */ true)) {
// [stack] CTOR? OBJ CTOR?
return false;
}
#ifdef DEBUG
propertyState_ = PropertyState::IndexKey;
#endif
return true;
}
bool PropertyEmitter::prepareForIndexPropValue() {
MOZ_ASSERT(propertyState_ == PropertyState::IndexKey);
// [stack] CTOR? OBJ CTOR? KEY
#ifdef DEBUG
propertyState_ = PropertyState::IndexValue;
#endif
return true;
}
bool PropertyEmitter::prepareForComputedPropKey(uint32_t keyPos, Kind kind) {
MOZ_ASSERT(propertyState_ == PropertyState::Start ||
propertyState_ == PropertyState::Init);
// [stack] CTOR? OBJ
if (!prepareForProp(keyPos,
/* isStatic_ = */ kind == Kind::Static,
/* isIndexOrComputed = */ true)) {
// [stack] CTOR? OBJ CTOR?
return false;
}
#ifdef DEBUG
propertyState_ = PropertyState::ComputedKey;
#endif
return true;
}
bool PropertyEmitter::prepareForComputedPropValue() {
MOZ_ASSERT(propertyState_ == PropertyState::ComputedKey);
// [stack] CTOR? OBJ CTOR? KEY
if (!bce_->emit1(JSOp::ToPropertyKey)) {
// [stack] CTOR? OBJ CTOR? KEY
return false;
}
#ifdef DEBUG
propertyState_ = PropertyState::ComputedValue;
#endif
return true;
}
bool PropertyEmitter::emitInitHomeObject() {
MOZ_ASSERT(propertyState_ == PropertyState::PropValue ||
propertyState_ == PropertyState::PrivateMethodValue ||
propertyState_ == PropertyState::PrivateStaticMethod ||
propertyState_ == PropertyState::IndexValue ||
propertyState_ == PropertyState::ComputedValue);
// [stack] CTOR? HOMEOBJ CTOR? KEY? FUN
// There are the following values on the stack conditionally, between
// HOMEOBJ and FUN:
// * the 2nd CTOR if isStatic_
// * KEY if isIndexOrComputed_
//
// JSOp::InitHomeObject uses one of the following:
// * HOMEOBJ if !isStatic_
// (`super.foo` points the super prototype property)
// * the 2nd CTOR if isStatic_
// (`super.foo` points the super constructor property)
if (!bce_->emitDupAt(1 + isIndexOrComputed_)) {
// [stack] # non-static method
// [stack] CTOR? HOMEOBJ CTOR KEY? FUN CTOR
// [stack] # static method
// [stack] CTOR? HOMEOBJ KEY? FUN HOMEOBJ
return false;
}
if (!bce_->emit1(JSOp::InitHomeObject)) {
// [stack] CTOR? HOMEOBJ CTOR? KEY? FUN
return false;
}
#ifdef DEBUG
if (propertyState_ == PropertyState::PropValue) {
propertyState_ = PropertyState::InitHomeObj;
} else if (propertyState_ == PropertyState::PrivateMethodValue) {
propertyState_ = PropertyState::InitHomeObjForPrivateMethod;
} else if (propertyState_ == PropertyState::PrivateStaticMethod) {
propertyState_ = PropertyState::InitHomeObjForPrivateStaticMethod;
} else if (propertyState_ == PropertyState::IndexValue) {
propertyState_ = PropertyState::InitHomeObjForIndex;
} else {
propertyState_ = PropertyState::InitHomeObjForComputed;
}
#endif
return true;
}
bool PropertyEmitter::emitInit(AccessorType accessorType,
TaggedParserAtomIndex key) {
switch (accessorType) {
case AccessorType::None:
return emitInit(isClass_ ? JSOp::InitHiddenProp : JSOp::InitProp, key);
case AccessorType::Getter:
return emitInit(
isClass_ ? JSOp::InitHiddenPropGetter : JSOp::InitPropGetter, key);
case AccessorType::Setter:
return emitInit(
isClass_ ? JSOp::InitHiddenPropSetter : JSOp::InitPropSetter, key);
}
MOZ_CRASH("Invalid op");
}
bool PropertyEmitter::emitInitIndexOrComputed(AccessorType accessorType) {
switch (accessorType) {
case AccessorType::None:
return emitInitIndexOrComputed(isClass_ ? JSOp::InitHiddenElem
: JSOp::InitElem);
case AccessorType::Getter:
return emitInitIndexOrComputed(isClass_ ? JSOp::InitHiddenElemGetter
: JSOp::InitElemGetter);
case AccessorType::Setter:
return emitInitIndexOrComputed(isClass_ ? JSOp::InitHiddenElemSetter
: JSOp::InitElemSetter);
}
MOZ_CRASH("Invalid op");
}
bool PropertyEmitter::emitPrivateStaticMethod(AccessorType accessorType) {
MOZ_ASSERT(isClass_);
switch (accessorType) {
case AccessorType::None:
return emitInitIndexOrComputed(JSOp::InitLockedElem);
case AccessorType::Getter:
return emitInitIndexOrComputed(JSOp::InitHiddenElemGetter);
case AccessorType::Setter:
return emitInitIndexOrComputed(JSOp::InitHiddenElemSetter);
}
MOZ_CRASH("Invalid op");
}
bool PropertyEmitter::emitInit(JSOp op, TaggedParserAtomIndex key) {
MOZ_ASSERT(propertyState_ == PropertyState::PropValue ||
propertyState_ == PropertyState::InitHomeObj);
MOZ_ASSERT(op == JSOp::InitProp || op == JSOp::InitHiddenProp ||
op == JSOp::InitPropGetter || op == JSOp::InitHiddenPropGetter ||
op == JSOp::InitPropSetter || op == JSOp::InitHiddenPropSetter);
// [stack] CTOR? OBJ CTOR? VAL
if (!bce_->emitAtomOp(op, key)) {
// [stack] CTOR? OBJ CTOR?
return false;
}
if (!emitPopClassConstructor()) {
return false;
}
#ifdef DEBUG
propertyState_ = PropertyState::Init;
#endif
return true;
}
bool PropertyEmitter::skipInit() {
MOZ_ASSERT(propertyState_ == PropertyState::PrivateMethodValue ||
propertyState_ == PropertyState::InitHomeObjForPrivateMethod);
#ifdef DEBUG
propertyState_ = PropertyState::Init;
#endif
return true;
}
bool PropertyEmitter::emitInitIndexOrComputed(JSOp op) {
MOZ_ASSERT(propertyState_ == PropertyState::IndexValue ||
propertyState_ == PropertyState::InitHomeObjForIndex ||
propertyState_ == PropertyState::ComputedValue ||
propertyState_ == PropertyState::InitHomeObjForComputed ||
propertyState_ == PropertyState::PrivateStaticMethod ||
propertyState_ ==
PropertyState::InitHomeObjForPrivateStaticMethod);
MOZ_ASSERT(op == JSOp::InitElem || op == JSOp::InitHiddenElem ||
op == JSOp::InitLockedElem || op == JSOp::InitElemGetter ||
op == JSOp::InitHiddenElemGetter || op == JSOp::InitElemSetter ||
op == JSOp::InitHiddenElemSetter);
// [stack] CTOR? OBJ CTOR? KEY VAL
if (!bce_->emit1(op)) {
// [stack] CTOR? OBJ CTOR?
return false;
}
if (!emitPopClassConstructor()) {
return false;
}
#ifdef DEBUG
propertyState_ = PropertyState::Init;
#endif
return true;
}
bool PropertyEmitter::emitPopClassConstructor() {
if (isStatic_) {
// [stack] CTOR HOMEOBJ CTOR
if (!bce_->emit1(JSOp::Pop)) {
// [stack] CTOR HOMEOBJ
return false;
}
}
return true;
}
ObjectEmitter::ObjectEmitter(BytecodeEmitter* bce) : PropertyEmitter(bce) {}
bool ObjectEmitter::emitObject(size_t propertyCount) {
MOZ_ASSERT(propertyState_ == PropertyState::Start);
MOZ_ASSERT(objectState_ == ObjectState::Start);
// [stack]
// Emit code for {p:a, '%q':b, 2:c} that is equivalent to constructing
// a new object and defining (in source order) each property on the object
// (or mutating the object's [[Prototype]], in the case of __proto__).
if (!bce_->emit1(JSOp::NewInit)) {
// [stack] OBJ
return false;
}
#ifdef DEBUG
objectState_ = ObjectState::Object;
#endif
return true;
}
bool ObjectEmitter::emitObjectWithTemplateOnStack() {
MOZ_ASSERT(propertyState_ == PropertyState::Start);
MOZ_ASSERT(objectState_ == ObjectState::Start);
#ifdef DEBUG
objectState_ = ObjectState::Object;
#endif
return true;
}
bool ObjectEmitter::emitEnd() {
MOZ_ASSERT(propertyState_ == PropertyState::Start ||
propertyState_ == PropertyState::Init);
MOZ_ASSERT(objectState_ == ObjectState::Object);
// [stack] OBJ
#ifdef DEBUG
objectState_ = ObjectState::End;
#endif
return true;
}
AutoSaveLocalStrictMode::AutoSaveLocalStrictMode(SharedContext* sc) : sc_(sc) {
savedStrictness_ = sc_->setLocalStrictMode(true);
}
AutoSaveLocalStrictMode::~AutoSaveLocalStrictMode() {
if (sc_) {
restore();
}
}
void AutoSaveLocalStrictMode::restore() {
MOZ_ALWAYS_TRUE(sc_->setLocalStrictMode(savedStrictness_));
sc_ = nullptr;
}
ClassEmitter::ClassEmitter(BytecodeEmitter* bce)
: PropertyEmitter(bce), strictMode_(bce->sc) {
isClass_ = true;
}
bool ClassEmitter::emitScope(LexicalScope::ParserData* scopeBindings) {
MOZ_ASSERT(propertyState_ == PropertyState::Start);
MOZ_ASSERT(classState_ == ClassState::Start);
tdzCache_.emplace(bce_);
innerScope_.emplace(bce_);
if (!innerScope_->enterLexical(bce_, ScopeKind::Lexical, scopeBindings)) {
return false;
}
#ifdef DEBUG
classState_ = ClassState::Scope;
#endif
return true;
}
bool ClassEmitter::emitBodyScope(ClassBodyScope::ParserData* scopeBindings) {
MOZ_ASSERT(propertyState_ == PropertyState::Start);
MOZ_ASSERT(classState_ == ClassState::Start ||
classState_ == ClassState::Scope);
bodyTdzCache_.emplace(bce_);
bodyScope_.emplace(bce_);
if (!bodyScope_->enterClassBody(bce_, ScopeKind::ClassBody, scopeBindings)) {
return false;
}
#ifdef DEBUG
classState_ = ClassState::BodyScope;
#endif
return true;
}
bool ClassEmitter::emitClass(TaggedParserAtomIndex name,
TaggedParserAtomIndex nameForAnonymousClass,
bool hasNameOnStack) {
MOZ_ASSERT(propertyState_ == PropertyState::Start);
MOZ_ASSERT(classState_ == ClassState::Start ||
classState_ == ClassState::Scope ||
classState_ == ClassState::BodyScope);
MOZ_ASSERT_IF(nameForAnonymousClass || hasNameOnStack, !name);
MOZ_ASSERT(!(nameForAnonymousClass && hasNameOnStack));
// [stack]
name_ = name;
nameForAnonymousClass_ = nameForAnonymousClass;
hasNameOnStack_ = hasNameOnStack;
isDerived_ = false;
if (!bce_->emit1(JSOp::NewInit)) {
// [stack] HOMEOBJ
return false;
}
#ifdef DEBUG
classState_ = ClassState::Class;
#endif
return true;
}
bool ClassEmitter::emitDerivedClass(TaggedParserAtomIndex name,
TaggedParserAtomIndex nameForAnonymousClass,
bool hasNameOnStack) {
MOZ_ASSERT(propertyState_ == PropertyState::Start);
MOZ_ASSERT(classState_ == ClassState::Start ||
classState_ == ClassState::Scope ||
classState_ == ClassState::BodyScope);
MOZ_ASSERT_IF(nameForAnonymousClass || hasNameOnStack, !name);
MOZ_ASSERT(!nameForAnonymousClass || !hasNameOnStack);
// [stack] HERITAGE
name_ = name;
nameForAnonymousClass_ = nameForAnonymousClass;
hasNameOnStack_ = hasNameOnStack;
isDerived_ = true;
InternalIfEmitter ifThenElse(bce_);
// Heritage must be null or a non-generator constructor
if (!bce_->emit1(JSOp::CheckClassHeritage)) {
// [stack] HERITAGE
return false;
}
// [IF] (heritage !== null)
if (!bce_->emit1(JSOp::Dup)) {
// [stack] HERITAGE HERITAGE
return false;
}
if (!bce_->emit1(JSOp::Null)) {
// [stack] HERITAGE HERITAGE NULL
return false;
}
if (!bce_->emit1(JSOp::StrictNe)) {
// [stack] HERITAGE NE
return false;
}
// [THEN] funProto = heritage, objProto = heritage.prototype
if (!ifThenElse.emitThenElse()) {
return false;
}
if (!bce_->emit1(JSOp::Dup)) {
// [stack] HERITAGE HERITAGE
return false;
}
if (!bce_->emitAtomOp(JSOp::GetProp,
TaggedParserAtomIndex::WellKnown::prototype())) {
// [stack] HERITAGE PROTO
return false;
}
// [ELSE] funProto = %FunctionPrototype%, objProto = null
if (!ifThenElse.emitElse()) {
return false;
}
if (!bce_->emit1(JSOp::Pop)) {
// [stack]
return false;
}
if (!bce_->emitBuiltinObject(BuiltinObjectKind::FunctionPrototype)) {
// [stack] PROTO
return false;
}
if (!bce_->emit1(JSOp::Null)) {
// [stack] PROTO NULL
return false;
}
// [ENDIF]
if (!ifThenElse.emitEnd()) {
return false;
}
if (!bce_->emit1(JSOp::ObjWithProto)) {
// [stack] HERITAGE HOMEOBJ
return false;
}
if (!bce_->emit1(JSOp::Swap)) {
// [stack] HOMEOBJ HERITAGE
return false;
}
#ifdef DEBUG
classState_ = ClassState::Class;
#endif
return true;
}
bool ClassEmitter::emitInitConstructor(bool needsHomeObject) {
MOZ_ASSERT(classState_ == ClassState::Class ||
classState_ == ClassState::InstanceMemberInitializersEnd);
// [stack] HOMEOBJ CTOR
if (needsHomeObject) {
if (!bce_->emitDupAt(1)) {
// [stack] HOMEOBJ CTOR HOMEOBJ
return false;
}
if (!bce_->emit1(JSOp::InitHomeObject)) {
// [stack] HOMEOBJ CTOR
return false;
}
}
if (!initProtoAndCtor()) {
// [stack] CTOR HOMEOBJ
return false;
}
#ifdef DEBUG
classState_ = ClassState::InitConstructor;
#endif
return true;
}
bool ClassEmitter::initProtoAndCtor() {
// [stack] NAME? HOMEOBJ CTOR
if (hasNameOnStack_) {
if (!bce_->emitDupAt(2)) {
// [stack] NAME HOMEOBJ CTOR NAME
return false;
}
if (!bce_->emit2(JSOp::SetFunName, uint8_t(FunctionPrefixKind::None))) {
// [stack] NAME HOMEOBJ CTOR
return false;
}
}
if (!bce_->emit1(JSOp::Swap)) {
// [stack] NAME? CTOR HOMEOBJ
return false;
}
if (!bce_->emit1(JSOp::Dup2)) {
// [stack] NAME? CTOR HOMEOBJ CTOR HOMEOBJ
return false;
}
if (!bce_->emitAtomOp(JSOp::InitLockedProp,
TaggedParserAtomIndex::WellKnown::prototype())) {
// [stack] NAME? CTOR HOMEOBJ CTOR
return false;
}
if (!bce_->emitAtomOp(JSOp::InitHiddenProp,
TaggedParserAtomIndex::WellKnown::constructor())) {
// [stack] NAME? CTOR HOMEOBJ
return false;
}
return true;
}
bool ClassEmitter::prepareForMemberInitializers(size_t numInitializers,
bool isStatic) {
MOZ_ASSERT_IF(!isStatic, classState_ == ClassState::Class);
MOZ_ASSERT_IF(isStatic, classState_ == ClassState::InitConstructor);
MOZ_ASSERT(memberState_ == MemberState::Start);
// .initializers is a variable that stores an array of lambdas containing
// code (the initializer) for each field. Upon an object's construction,
// these lambdas will be called, defining the values.
auto initializers =
isStatic ? TaggedParserAtomIndex::WellKnown::dot_staticInitializers_()
: TaggedParserAtomIndex::WellKnown::dot_initializers_();
initializersAssignment_.emplace(bce_, initializers,
NameOpEmitter::Kind::Initialize);
if (!initializersAssignment_->prepareForRhs()) {
return false;
}
if (!bce_->emitUint32Operand(JSOp::NewArray, numInitializers)) {
// [stack] ARRAY
return false;
}
initializerIndex_ = 0;
#ifdef DEBUG
if (isStatic) {
classState_ = ClassState::StaticMemberInitializers;
} else {
classState_ = ClassState::InstanceMemberInitializers;
}
numInitializers_ = numInitializers;
#endif
return true;
}
bool ClassEmitter::prepareForMemberInitializer() {
MOZ_ASSERT(classState_ == ClassState::InstanceMemberInitializers ||
classState_ == ClassState::StaticMemberInitializers);
MOZ_ASSERT(memberState_ == MemberState::Start);
#ifdef DEBUG
memberState_ = MemberState::Initializer;
#endif
return true;
}
bool ClassEmitter::emitMemberInitializerHomeObject(bool isStatic) {
MOZ_ASSERT(memberState_ == MemberState::Initializer);
// [stack] OBJ HERITAGE? ARRAY METHOD
// or:
// [stack] CTOR HOMEOBJ ARRAY METHOD
if (isStatic) {
if (!bce_->emitDupAt(3)) {
// [stack] CTOR HOMEOBJ ARRAY METHOD CTOR
return false;
}
} else {
if (!bce_->emitDupAt(isDerived_ ? 3 : 2)) {
// [stack] OBJ HERITAGE? ARRAY METHOD OBJ
return false;
}
}
if (!bce_->emit1(JSOp::InitHomeObject)) {
// [stack] OBJ HERITAGE? ARRAY METHOD
// or:
// [stack] CTOR HOMEOBJ ARRAY METHOD
return false;
}
#ifdef DEBUG
memberState_ = MemberState::InitializerWithHomeObject;
#endif
return true;
}
bool ClassEmitter::emitStoreMemberInitializer() {
MOZ_ASSERT(memberState_ == MemberState::Initializer ||
memberState_ == MemberState::InitializerWithHomeObject);
MOZ_ASSERT(initializerIndex_ < numInitializers_);
// [stack] HOMEOBJ HERITAGE? ARRAY METHOD
if (!bce_->emitUint32Operand(JSOp::InitElemArray, initializerIndex_)) {
// [stack] HOMEOBJ HERITAGE? ARRAY
return false;
}
initializerIndex_++;
#ifdef DEBUG
memberState_ = MemberState::Start;
#endif
return true;
}
bool ClassEmitter::emitMemberInitializersEnd() {
MOZ_ASSERT(propertyState_ == PropertyState::Start ||
propertyState_ == PropertyState::Init);
MOZ_ASSERT(classState_ == ClassState::InstanceMemberInitializers ||
classState_ == ClassState::StaticMemberInitializers);
MOZ_ASSERT(memberState_ == MemberState::Start);
MOZ_ASSERT(initializerIndex_ == numInitializers_);
if (!initializersAssignment_->emitAssignment()) {
// [stack] HOMEOBJ HERITAGE? ARRAY
return false;
}
initializersAssignment_.reset();
if (!bce_->emit1(JSOp::Pop)) {
// [stack] HOMEOBJ HERITAGE?
return false;
}
#ifdef DEBUG
if (classState_ == ClassState::InstanceMemberInitializers) {
classState_ = ClassState::InstanceMemberInitializersEnd;
} else {
classState_ = ClassState::StaticMemberInitializersEnd;
}
#endif
return true;
}
#ifdef ENABLE_DECORATORS
bool ClassEmitter::prepareForExtraInitializers(
TaggedParserAtomIndex initializers) {
MOZ_ASSERT(
initializers ==
TaggedParserAtomIndex::WellKnown::dot_instanceExtraInitializers_());
NameOpEmitter noe(bce_, initializers, NameOpEmitter::Kind::Initialize);
if (!noe.prepareForRhs()) {
return false;
}
// Because the initializers are created while executing decorators, we don't
// know beforehand how many there will be.
if (!bce_->emitUint32Operand(JSOp::NewArray, 0)) {
// [stack] ARRAY
return false;
}
if (!noe.emitAssignment()) {
// [stack] ARRAY
return false;
}
return bce_->emit1(JSOp::Pop);
// [stack]
}
#endif
bool ClassEmitter::emitBinding() {
MOZ_ASSERT(propertyState_ == PropertyState::Start ||
propertyState_ == PropertyState::Init);
MOZ_ASSERT(classState_ == ClassState::InitConstructor ||
classState_ == ClassState::InstanceMemberInitializersEnd ||
classState_ == ClassState::StaticMemberInitializersEnd);
// [stack] CTOR HOMEOBJ
if (!bce_->emit1(JSOp::Pop)) {
// [stack] CTOR
return false;
}
if (name_) {
MOZ_ASSERT(innerScope_.isSome());
if (!bce_->emitLexicalInitialization(name_)) {
// [stack] CTOR
return false;
}
}
// [stack] CTOR
#ifdef DEBUG
classState_ = ClassState::BoundName;
#endif
return true;
}
#ifdef ENABLE_DECORATORS
bool ClassEmitter::prepareForDecorators() { return leaveBodyAndInnerScope(); }
#endif
bool ClassEmitter::leaveBodyAndInnerScope() {
if (bodyScope_.isSome()) {
MOZ_ASSERT(bodyTdzCache_.isSome());
if (!bodyScope_->leave(bce_)) {
return false;
}
bodyScope_.reset();
bodyTdzCache_.reset();
}
if (innerScope_.isSome()) {
MOZ_ASSERT(tdzCache_.isSome());
if (!innerScope_->leave(bce_)) {
return false;
}
innerScope_.reset();
tdzCache_.reset();
} else {
MOZ_ASSERT(tdzCache_.isNothing());
}
return true;
}
bool ClassEmitter::emitEnd(Kind kind) {
MOZ_ASSERT(classState_ == ClassState::BoundName);
// [stack] CTOR
#ifndef ENABLE_DECORATORS
if (!leaveBodyAndInnerScope()) {
return false;
}
#endif
if (kind == Kind::Declaration) {
MOZ_ASSERT(name_);
if (!bce_->emitLexicalInitialization(name_)) {
// [stack] CTOR
return false;
}
// Only class statements make outer bindings, and they do not leave
// themselves on the stack.
if (!bce_->emit1(JSOp::Pop)) {
// [stack]
return false;
}
}
// [stack] # class declaration
// [stack]
// [stack] # class expression
// [stack] CTOR
strictMode_.restore();
#ifdef DEBUG
classState_ = ClassState::End;
#endif
return true;
}