diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /js/src/frontend/ObjectEmitter.cpp | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
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 | 913 |
1 files changed, 913 insertions, 0 deletions
diff --git a/js/src/frontend/ObjectEmitter.cpp b/js/src/frontend/ObjectEmitter.cpp new file mode 100644 index 0000000000..96bcad8dc6 --- /dev/null +++ b/js/src/frontend/ObjectEmitter.cpp @@ -0,0 +1,913 @@ +/* -*- 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 "gc/AllocKind.h" // AllocKind +#include "js/Id.h" // jsid +#include "js/Value.h" // UndefinedHandleValue +#include "vm/BytecodeUtil.h" // IsHiddenInitOp +#include "vm/FunctionPrefixKind.h" // FunctionPrefixKind +#include "vm/JSContext.h" // JSContext +#include "vm/NativeObject.h" // NativeDefineDataProperty +#include "vm/ObjectGroup.h" // TenuredObject +#include "vm/Opcodes.h" // JSOp +#include "vm/Runtime.h" // cx->parserNames() +#include "vm/SharedStencil.h" // GCThingIndex + +#include "gc/ObjectKind-inl.h" // GetGCObjectKind +#include "vm/JSAtom-inl.h" // AtomToId +#include "vm/JSObject-inl.h" // NewBuiltinClassInstance + +using namespace js; +using namespace js::frontend; + +using mozilla::Maybe; + +PropertyEmitter::PropertyEmitter(BytecodeEmitter* bce) : bce_(bce) {} + +bool PropertyEmitter::prepareForProtoValue(const Maybe<uint32_t>& keyPos) { + MOZ_ASSERT(propertyState_ == PropertyState::Start || + propertyState_ == PropertyState::Init); + + // [stack] CTOR? OBJ CTOR? + + if (keyPos) { + 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( + const Maybe<uint32_t>& spreadPos) { + MOZ_ASSERT(propertyState_ == PropertyState::Start || + propertyState_ == PropertyState::Init); + + // [stack] OBJ + + if (spreadPos) { + 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( + const Maybe<uint32_t>& keyPos, bool isStatic, bool isIndexOrComputed) { + isStatic_ = isStatic; + isIndexOrComputed_ = isIndexOrComputed; + + // [stack] CTOR? OBJ + + if (keyPos) { + 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::prepareForPropValue(const Maybe<uint32_t>& keyPos, + Kind kind /* = Kind::Prototype */) { + 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( + const Maybe<uint32_t>& keyPos, Kind kind /* = Kind::Prototype */) { + 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( + const Maybe<uint32_t>& keyPos, Kind kind /* = Kind::Prototype */) { + 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::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::IndexValue) { + propertyState_ = PropertyState::InitHomeObjForIndex; + } else { + propertyState_ = PropertyState::InitHomeObjForComputed; + } +#endif + return true; +} + +bool PropertyEmitter::emitInit(AccessorType accessorType, + const ParserAtom* 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); + default: + 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); + default: + MOZ_CRASH("Invalid op"); + } +} + +bool PropertyEmitter::emitInit(JSOp op, const ParserAtom* 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::emitInitIndexOrComputed(JSOp op) { + MOZ_ASSERT(propertyState_ == PropertyState::IndexValue || + propertyState_ == PropertyState::InitHomeObjForIndex || + propertyState_ == PropertyState::ComputedValue || + propertyState_ == PropertyState::InitHomeObjForComputed); + + MOZ_ASSERT(op == JSOp::InitElem || op == JSOp::InitHiddenElem || + 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), + name_(nullptr), + nameForAnonymousClass_(nullptr) { + 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(LexicalScope::ParserData* scopeBindings) { + MOZ_ASSERT(propertyState_ == PropertyState::Start); + MOZ_ASSERT(classState_ == ClassState::Start || + classState_ == ClassState::Scope); + + bodyTdzCache_.emplace(bce_); + + bodyScope_.emplace(bce_); + if (!bodyScope_->enterLexical(bce_, ScopeKind::ClassBody, scopeBindings)) { + return false; + } + +#ifdef DEBUG + classState_ = ClassState::BodyScope; +#endif + + return true; +} + +bool ClassEmitter::emitClass(const ParserAtom* name, + const ParserAtom* 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(const ParserAtom* name, + const ParserAtom* 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, bce_->cx->parserNames().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(propertyState_ == PropertyState::Start); + 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::emitInitDefaultConstructor(uint32_t classStart, + uint32_t classEnd) { + MOZ_ASSERT(propertyState_ == PropertyState::Start); + MOZ_ASSERT(classState_ == ClassState::Class); + + const ParserAtom* className = name_; + if (!className) { + if (nameForAnonymousClass_) { + className = nameForAnonymousClass_; + } else { + className = bce_->cx->parserNames().empty; + } + } + + GCThingIndex atomIndex; + if (!bce_->makeAtomIndex(className, &atomIndex)) { + return false; + } + + // The default constructor opcodes below will synthesize new scripts with + // line/column at start of class definition. + if (!bce_->updateSourceCoordNotes(classStart)) { + return false; + } + + // In the case of default class constructors, emit the start and end + // offsets in the source buffer as source notes so that when we + // actually make the constructor during execution, we can give it the + // correct toString output. + BytecodeOffset off; + if (isDerived_) { + // [stack] HERITAGE PROTO + if (!bce_->emitN(JSOp::DerivedConstructor, 12, &off)) { + // [stack] HOMEOBJ CTOR + return false; + } + } else { + // [stack] HOMEOBJ + if (!bce_->emitN(JSOp::ClassConstructor, 12, &off)) { + // [stack] HOMEOBJ CTOR + return false; + } + } + SetClassConstructorOperands(bce_->bytecodeSection().code(off), atomIndex, + classStart, classEnd); + + 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, + bce_->cx->parserNames().prototype)) { + // [stack] NAME? CTOR HOMEOBJ CTOR + return false; + } + if (!bce_->emitAtomOp(JSOp::InitHiddenProp, + bce_->cx->parserNames().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. + const ParserName* initializers = + isStatic ? bce_->cx->parserNames().dotStaticInitializers + : bce_->cx->parserNames().dotInitializers; + 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; +} + +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; +} + +bool ClassEmitter::emitEnd(Kind kind) { + MOZ_ASSERT(classState_ == ClassState::BoundName); + // [stack] CTOR + + 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(kind == Kind::Expression); + MOZ_ASSERT(tdzCache_.isNothing()); + } + + 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; +} |