/* -*- 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& 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& 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& 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& 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& 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& 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; }