Files
basilisk55/js/src/frontend/ObjectEmitter.cpp
T

862 lines
25 KiB
C++

/* -*- 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 "jsatominlines.h" // AtomToId
#include "jsgcinlines.h" // GetGCObjectKind
#include "jsobjinlines.h" // NewBuiltinClassInstance
#include "frontend/BytecodeEmitter.h" // BytecodeEmitter
#include "frontend/SharedContext.h" // SharedContext
#include "frontend/SourceNotes.h" // SRC_*
#include "gc/Heap.h" // AllocKind
#include "js/Id.h" // jsid
#include "js/Value.h" // UndefinedHandleValue
#include "vm/NativeObject.h" // NativeDefineDataProperty
#include "vm/ObjectGroup.h" // TenuredObject
#include "vm/Runtime.h" // JSAtomState (cx->names())
using namespace js;
using namespace js::frontend;
using mozilla::Maybe;
PropertyEmitter::PropertyEmitter(BytecodeEmitter* bce)
: bce_(bce), obj_(bce->cx) {}
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;
}
obj_ = nullptr;
#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;
}
obj_ = nullptr;
#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
obj_ = nullptr;
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
obj_ = nullptr;
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_TOID)) {
// [stack] CTOR? OBJ CTOR? KEY
return false;
}
#ifdef DEBUG
propertyState_ = PropertyState::ComputedValue;
#endif
return true;
}
bool PropertyEmitter::emitInitHomeObject(FunctionAsyncKind kind /* = FunctionAsyncKind::SyncFunction */)
{
MOZ_ASSERT(propertyState_ == PropertyState::PropValue ||
propertyState_ == PropertyState::IndexValue ||
propertyState_ == PropertyState::ComputedValue);
// [stack] CTOR? HOMEOBJ CTOR? KEY? FUN
bool isAsync = kind == FunctionAsyncKind::AsyncFunction;
if (isAsync) {
// [stack] CTOR? HOMEOBJ CTOR? KEY? UNWRAPPED WRAPPED
if (!bce_->emit1(JSOP_SWAP)) {
// [stack] CTOR? HOMEOBJ CTOR? KEY? WRAPPED UNWRAPPED
return false;
}
}
if (!bce_->emit2(JSOP_INITHOMEOBJECT, isIndexOrComputed_ + isAsync)) {
// [stack] CTOR? HOMEOBJ CTOR? KEY? WRAPPED? FUN
return false;
}
if (isAsync) {
if (!bce_->emit1(JSOP_POP)) {
// [stack] CTOR? HOMEOBJ CTOR? KEY? WRAPPED
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::emitInitProp(JS::Handle<JSAtom*> key,
bool isPropertyAnonFunctionOrClass /* = false */,
JS::Handle<JSFunction*> anonFunction /* = nullptr */)
{
return emitInit(isClass_ ? JSOP_INITHIDDENPROP : JSOP_INITPROP, key,
isPropertyAnonFunctionOrClass, anonFunction);
}
bool PropertyEmitter::emitInitGetter(JS::Handle<JSAtom*> key)
{
obj_ = nullptr;
return emitInit(isClass_ ? JSOP_INITHIDDENPROP_GETTER : JSOP_INITPROP_GETTER,
key, false, nullptr);
}
bool PropertyEmitter::emitInitSetter(JS::Handle<JSAtom*> key)
{
obj_ = nullptr;
return emitInit(isClass_ ? JSOP_INITHIDDENPROP_SETTER : JSOP_INITPROP_SETTER,
key, false, nullptr);
}
bool PropertyEmitter::emitInitIndexProp(bool isPropertyAnonFunctionOrClass /* = false */)
{
return emitInitIndexOrComputed(isClass_ ? JSOP_INITHIDDENELEM : JSOP_INITELEM,
FunctionPrefixKind::None,
isPropertyAnonFunctionOrClass);
}
bool PropertyEmitter::emitInitIndexGetter()
{
obj_ = nullptr;
return emitInitIndexOrComputed(
isClass_ ? JSOP_INITHIDDENELEM_GETTER : JSOP_INITELEM_GETTER,
FunctionPrefixKind::Get, false);
}
bool PropertyEmitter::emitInitIndexSetter()
{
obj_ = nullptr;
return emitInitIndexOrComputed(
isClass_ ? JSOP_INITHIDDENELEM_SETTER : JSOP_INITELEM_SETTER,
FunctionPrefixKind::Set, false);
}
bool PropertyEmitter::emitInitComputedProp(bool isPropertyAnonFunctionOrClass /* = false */)
{
return emitInitIndexOrComputed(isClass_ ? JSOP_INITHIDDENELEM : JSOP_INITELEM,
FunctionPrefixKind::None,
isPropertyAnonFunctionOrClass);
}
bool PropertyEmitter::emitInitComputedGetter()
{
obj_ = nullptr;
return emitInitIndexOrComputed(isClass_ ? JSOP_INITHIDDENELEM_GETTER : JSOP_INITELEM_GETTER,
FunctionPrefixKind::Get, true);
}
bool PropertyEmitter::emitInitComputedSetter()
{
obj_ = nullptr;
return emitInitIndexOrComputed(isClass_ ? JSOP_INITHIDDENELEM_SETTER : JSOP_INITELEM_SETTER,
FunctionPrefixKind::Set, true);
}
bool PropertyEmitter::emitInit(JSOp op, JS::Handle<JSAtom*> key,
bool isPropertyAnonFunctionOrClass,
JS::Handle<JSFunction*> anonFunction)
{
MOZ_ASSERT(propertyState_ == PropertyState::PropValue ||
propertyState_ == PropertyState::InitHomeObj);
MOZ_ASSERT(op == JSOP_INITPROP || op == JSOP_INITHIDDENPROP ||
op == JSOP_INITPROP_GETTER || op == JSOP_INITHIDDENPROP_GETTER ||
op == JSOP_INITPROP_SETTER || op == JSOP_INITHIDDENPROP_SETTER);
// [stack] CTOR? OBJ CTOR? VAL
uint32_t index;
if (!bce_->makeAtomIndex(key, &index))
return false;
if (obj_) {
MOZ_ASSERT(!IsHiddenInitOp(op));
MOZ_ASSERT(!obj_->inDictionaryMode());
JS::RootedId id(bce_->cx, AtomToId(key));
if (!NativeDefineProperty(bce_->cx, obj_, id, UndefinedHandleValue, nullptr, nullptr,
JSPROP_ENUMERATE))
return false;
if (obj_->inDictionaryMode())
obj_ = nullptr;
}
if (isPropertyAnonFunctionOrClass) {
MOZ_ASSERT(op == JSOP_INITPROP || op == JSOP_INITHIDDENPROP);
if (anonFunction) {
if (!bce_->setFunName(anonFunction, key))
return false;
} else {
// NOTE: This is setting the constructor's name of the class which is
// the property value. Not of the enclosing class.
if (!bce_->emitSetClassConstructorName(key)) {
// [stack] CTOR? OBJ CTOR? FUN
return false;
}
}
}
if (!bce_->emitIndex32(op, index)) {
// [stack] CTOR? OBJ CTOR?
return false;
}
if (!emitPopClassConstructor())
return false;
#ifdef DEBUG
propertyState_ = PropertyState::Init;
#endif
return true;
}
bool PropertyEmitter::emitInitIndexOrComputed(JSOp op, FunctionPrefixKind prefixKind,
bool isPropertyAnonFunctionOrClass)
{
MOZ_ASSERT(propertyState_ == PropertyState::IndexValue ||
propertyState_ == PropertyState::InitHomeObjForIndex ||
propertyState_ == PropertyState::ComputedValue ||
propertyState_ == PropertyState::InitHomeObjForComputed);
MOZ_ASSERT(op == JSOP_INITELEM || op == JSOP_INITHIDDENELEM ||
op == JSOP_INITELEM_GETTER || op == JSOP_INITHIDDENELEM_GETTER ||
op == JSOP_INITELEM_SETTER || op == JSOP_INITHIDDENELEM_SETTER);
// [stack] CTOR? OBJ CTOR? KEY VAL
if (isPropertyAnonFunctionOrClass) {
if (!bce_->emitDupAt(1)) {
// [stack] CTOR? OBJ CTOR? KEY FUN FUN
return false;
}
if (!bce_->emit2(JSOP_SETFUNNAME, uint8_t(prefixKind))) {
// [stack] CTOR? OBJ CTOR? KEY FUN
return false;
}
}
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__).
top_ = bce_->offset();
if (!bce_->emitNewInit(JSProto_Object)) {
// [stack] OBJ
return false;
}
// Try to construct the shape of the object as we go, so we can emit a
// JSOP_NEWOBJECT with the final shape instead.
// In the case of computed property names and indices, we cannot fix the
// shape at bytecode compile time. When the shape cannot be determined,
// |obj| is nulled out.
// No need to do any guessing for the object kind, since we know the upper
// bound of how many properties we plan to have.
gc::AllocKind kind = gc::GetGCObjectKind(propertyCount);
obj_ = NewBuiltinClassInstance<PlainObject>(bce_->cx, kind, TenuredObject);
if (!obj_)
return false;
#ifdef DEBUG
objectState_ = ObjectState::Object;
#endif
return true;
}
bool ClassEmitter::prepareForFieldInitializers(size_t numFields)
{
MOZ_ASSERT(classState_ == ClassState::Class);
// .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.
initializersAssignment_.emplace(bce_, bce_->cx->names().dotInitializers,
NameOpEmitter::Kind::Initialize);
if (!initializersAssignment_->prepareForRhs()) {
return false;
}
if (!bce_->emitUint32Operand(JSOP_NEWARRAY, numFields)) {
// [stack] HOMEOBJ HERITAGE? ARRAY
return false;
}
MOZ_ASSERT(fieldIndex_ == 0);
#ifdef DEBUG
classState_ = ClassState::FieldInitializers;
numFields_ = numFields;
#endif
return true;
}
bool ClassEmitter::emitFieldInitializerHomeObject()
{
MOZ_ASSERT(classState_ == ClassState::FieldInitializers);
// [stack] OBJ HERITAGE? ARRAY METHOD
if (!bce_->emit2(JSOP_INITHOMEOBJECT, isDerived_ ? 2 : 1)) {
// [stack] OBJ HERITAGE? ARRAY METHOD
return false;
}
#ifdef DEBUG
classState_ = ClassState::FieldInitializerWithHomeObject;
#endif
return true;
}
bool ClassEmitter::emitStoreFieldInitializer()
{
MOZ_ASSERT(classState_ == ClassState::FieldInitializers ||
classState_ == ClassState::FieldInitializerWithHomeObject);
MOZ_ASSERT(fieldIndex_ < numFields_);
// [stack] HOMEOBJ HERITAGE? ARRAY METHOD
if (!bce_->emitUint32Operand(JSOP_INITELEM_ARRAY, fieldIndex_)) {
// [stack] HOMEOBJ HERITAGE? ARRAY
return false;
}
fieldIndex_++;
#ifdef DEBUG
classState_ = ClassState::FieldInitializers;
#endif
return true;
}
bool ClassEmitter::emitFieldInitializersEnd()
{
MOZ_ASSERT(propertyState_ == PropertyState::Start ||
propertyState_ == PropertyState::Init);
MOZ_ASSERT(classState_ == ClassState::FieldInitializers ||
classState_ == ClassState::FieldInitializerWithHomeObject);
MOZ_ASSERT(fieldIndex_ == numFields_);
if (!initializersAssignment_->emitAssignment()) {
// [stack] HOMEOBJ HERITAGE? ARRAY
return false;
}
initializersAssignment_.reset();
if (!bce_->emit1(JSOP_POP)) {
// [stack] HOMEOBJ HERITAGE?
return false;
}
#ifdef DEBUG
classState_ = ClassState::FieldInitializersEnd;
#endif
return true;
}
bool ObjectEmitter::emitEnd()
{
MOZ_ASSERT(propertyState_ == PropertyState::Start ||
propertyState_ == PropertyState::Init);
MOZ_ASSERT(objectState_ == ObjectState::Object);
// [stack] OBJ
if (obj_) {
// The object survived and has a predictable shape: update the original
// bytecode.
if (!bce_->replaceNewInitWithNewObject(obj_, top_)) {
// [stack] OBJ
return false;
}
}
#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_(bce->cx)
{
isClass_ = true;
}
bool ClassEmitter::emitScope(JS::Handle<LexicalScope::Data*> 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::emitClass(JS::Handle<JSAtom*> name)
{
MOZ_ASSERT(propertyState_ == PropertyState::Start);
MOZ_ASSERT(classState_ == ClassState::Start ||
classState_ == ClassState::Scope);
// [stack]
setName(name);
isDerived_ = false;
if (!bce_->emitNewInit(JSProto_Object)) {
// [stack] HOMEOBJ
return false;
}
#ifdef DEBUG
classState_ = ClassState::Class;
#endif
return true;
}
bool ClassEmitter::emitDerivedClass(JS::Handle<JSAtom*> name)
{
MOZ_ASSERT(propertyState_ == PropertyState::Start);
MOZ_ASSERT(classState_ == ClassState::Start ||
classState_ == ClassState::Scope);
// [stack] HERITAGE
setName(name);
isDerived_ = true;
if (!bce_->emit1(JSOP_CLASSHERITAGE)) {
// [stack] funcProto objProto
return false;
}
if (!bce_->emit1(JSOP_OBJWITHPROTO)) {
// [stack] funcProto HOMEOBJ
return false;
}
// JSOP_CLASSHERITAGE leaves both protos on the stack. After
// creating the prototype, swap it to the bottom to make the
// constructor.
if (!bce_->emit1(JSOP_SWAP)) {
// [stack] HOMEOBJ funcProto
return false;
}
#ifdef DEBUG
classState_ = ClassState::Class;
#endif
return true;
}
void ClassEmitter::setName(JS::Handle<JSAtom*> name)
{
name_ = name;
if (!name_)
name_ = bce_->cx->names().empty;
}
bool ClassEmitter::emitInitConstructor(bool needsHomeObject)
{
MOZ_ASSERT(propertyState_ == PropertyState::Start);
MOZ_ASSERT(classState_ == ClassState::Class ||
classState_ == ClassState::FieldInitializersEnd);
// [stack] HOMEOBJ CTOR
if (needsHomeObject) {
if (!bce_->emit2(JSOP_INITHOMEOBJECT, 0)) {
// [stack] HOMEOBJ CTOR
return false;
}
}
if (!initProtoAndCtor()) {
// [stack] CTOR HOMEOBJ
return false;
}
#ifdef DEBUG
classState_ = ClassState::InitConstructor;
#endif
return true;
}
bool ClassEmitter::emitInitDefaultConstructor(const Maybe<uint32_t>& classStart,
const Maybe<uint32_t>& classEnd)
{
MOZ_ASSERT(propertyState_ == PropertyState::Start);
MOZ_ASSERT(classState_ == ClassState::Class ||
classState_ == ClassState::FieldInitializersEnd);
if (classStart && classEnd) {
// 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.
if (!bce_->newSrcNote3(SRC_CLASS_SPAN, ptrdiff_t(*classStart),
ptrdiff_t(*classEnd))) {
return false;
}
}
if (isDerived_) {
// [stack] HERITAGE PROTO
if (!bce_->emitAtomOp(name_, JSOP_DERIVEDCONSTRUCTOR)) {
// [stack] HOMEOBJ CTOR
return false;
}
} else {
// [stack] HOMEOBJ
if (!bce_->emitAtomOp(name_, JSOP_CLASSCONSTRUCTOR)) {
// [stack] HOMEOBJ CTOR
return false;
}
}
if (!initProtoAndCtor()) {
// [stack] CTOR HOMEOBJ
return false;
}
#ifdef DEBUG
classState_ = ClassState::InitConstructor;
#endif
return true;
}
bool ClassEmitter::initProtoAndCtor()
{
// [stack] HOMEOBJ CTOR
if (!bce_->emit1(JSOP_SWAP)) {
// [stack] CTOR HOMEOBJ
return false;
}
if (!bce_->emit1(JSOP_DUP2)) {
// [stack] CTOR HOMEOBJ CTOR HOMEOBJ
return false;
}
if (!bce_->emitAtomOp(bce_->cx->names().prototype, JSOP_INITLOCKEDPROP)) {
// [stack] CTOR HOMEOBJ CTOR
return false;
}
if (!bce_->emitAtomOp(bce_->cx->names().constructor, JSOP_INITHIDDENPROP)) {
// [stack] CTOR HOMEOBJ
return false;
}
return true;
}
bool ClassEmitter::emitEnd(Kind kind)
{
MOZ_ASSERT(propertyState_ == PropertyState::Start ||
propertyState_ == PropertyState::Init);
MOZ_ASSERT(classState_ == ClassState::InitConstructor ||
classState_ == ClassState::FieldInitializersEnd);
// [stack] CTOR HOMEOBJ
if (!bce_->emit1(JSOP_POP)) {
// [stack] CTOR
return false;
}
if (name_ != bce_->cx->names().empty) {
MOZ_ASSERT(tdzCache_.isSome());
MOZ_ASSERT(innerScope_.isSome());
if (!bce_->emitLexicalInitialization(name_)) {
// [stack] CTOR
return false;
}
// Pop the inner scope.
if (!innerScope_->leave(bce_))
return false;
innerScope_.reset();
if (kind == Kind::Declaration) {
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;
}
}
tdzCache_.reset();
} else if (innerScope_.isSome()) {
// [stack] CTOR
MOZ_ASSERT(kind == Kind::Expression);
MOZ_ASSERT(tdzCache_.isSome());
if (!innerScope_->leave(bce_))
return false;
innerScope_.reset();
tdzCache_.reset();
}else {
// [stack] CTOR
MOZ_ASSERT(kind == Kind::Expression);
MOZ_ASSERT(tdzCache_.isNothing());
}
// [stack] # class declaration
// [stack]
// [stack] # class expression
// [stack] CTOR
strictMode_.restore();
#ifdef DEBUG
classState_ = ClassState::End;
#endif
return true;
}