diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /js/src/frontend/ObjectEmitter.cpp | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/frontend/ObjectEmitter.cpp')
-rw-r--r-- | js/src/frontend/ObjectEmitter.cpp | 938 |
1 files changed, 938 insertions, 0 deletions
diff --git a/js/src/frontend/ObjectEmitter.cpp b/js/src/frontend/ObjectEmitter.cpp new file mode 100644 index 0000000000..d507c6f6d4 --- /dev/null +++ b/js/src/frontend/ObjectEmitter.cpp @@ -0,0 +1,938 @@ +/* -*- 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 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#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) { + // TODO: Add support for static and class extra initializers, see bug 1868220 + // and bug 1868221. + 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; +} |