summaryrefslogtreecommitdiffstats
path: root/js/src/frontend/ElemOpEmitter.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--js/src/frontend/ElemOpEmitter.cpp304
1 files changed, 304 insertions, 0 deletions
diff --git a/js/src/frontend/ElemOpEmitter.cpp b/js/src/frontend/ElemOpEmitter.cpp
new file mode 100644
index 0000000000..53bf366325
--- /dev/null
+++ b/js/src/frontend/ElemOpEmitter.cpp
@@ -0,0 +1,304 @@
+/* -*- 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/ElemOpEmitter.h"
+
+#include "frontend/BytecodeEmitter.h"
+#include "frontend/SharedContext.h"
+#include "vm/Opcodes.h"
+#include "vm/ThrowMsgKind.h" // ThrowMsgKind
+
+using namespace js;
+using namespace js::frontend;
+
+ElemOpEmitter::ElemOpEmitter(BytecodeEmitter* bce, Kind kind, ObjKind objKind,
+ NameVisibility visibility)
+ : bce_(bce), kind_(kind), objKind_(objKind), visibility_(visibility) {
+ // Can't access private names of super!
+ MOZ_ASSERT_IF(visibility == NameVisibility::Private,
+ objKind != ObjKind::Super);
+}
+
+bool ElemOpEmitter::prepareForObj() {
+ MOZ_ASSERT(state_ == State::Start);
+
+#ifdef DEBUG
+ state_ = State::Obj;
+#endif
+ return true;
+}
+
+bool ElemOpEmitter::prepareForKey() {
+ MOZ_ASSERT(state_ == State::Obj);
+
+ if (!isSuper() && isIncDec()) {
+ if (!bce_->emit1(JSOp::CheckObjCoercible)) {
+ // [stack] OBJ
+ return false;
+ }
+ }
+ if (isCall()) {
+ if (!bce_->emit1(JSOp::Dup)) {
+ // [stack] # if Super
+ // [stack] THIS THIS
+ // [stack] # otherwise
+ // [stack] OBJ OBJ
+ return false;
+ }
+ }
+
+#ifdef DEBUG
+ state_ = State::Key;
+#endif
+ return true;
+}
+
+bool ElemOpEmitter::emitPrivateGuard() {
+ MOZ_ASSERT(state_ == State::Key);
+
+ if (!isPrivate()) {
+ return true;
+ }
+
+ if (isPropInit()) {
+ // [stack] OBJ KEY
+ if (!bce_->emitCheckPrivateField(ThrowCondition::ThrowHas,
+ ThrowMsgKind::PrivateDoubleInit)) {
+ // [stack] OBJ KEY BOOL
+ return false;
+ }
+ } else {
+ if (!bce_->emitCheckPrivateField(ThrowCondition::ThrowHasNot,
+ isPrivateGet()
+ ? ThrowMsgKind::MissingPrivateOnGet
+ : ThrowMsgKind::MissingPrivateOnSet)) {
+ // [stack] OBJ KEY BOOL
+ return false;
+ }
+ }
+
+ // CheckPrivate leaves the result of the HasOwnCheck on the stack. Pop it off.
+ return bce_->emit1(JSOp::Pop);
+ // [stack] OBJ KEY
+}
+
+bool ElemOpEmitter::emitGet() {
+ MOZ_ASSERT(state_ == State::Key);
+
+ if (isIncDec() || isCompoundAssignment()) {
+ if (!bce_->emit1(JSOp::ToPropertyKey)) {
+ // [stack] # if Super
+ // [stack] THIS KEY
+ // [stack] # otherwise
+ // [stack] OBJ KEY
+ return false;
+ }
+ }
+
+ if (!emitPrivateGuard()) {
+ return false;
+ }
+
+ if (isSuper()) {
+ if (!bce_->emitSuperBase()) {
+ // [stack] THIS? THIS KEY SUPERBASE
+ return false;
+ }
+ }
+ if (isIncDec() || isCompoundAssignment()) {
+ if (isSuper()) {
+ if (!bce_->emitDupAt(2, 3)) {
+ // [stack] THIS KEY SUPERBASE THIS KEY SUPERBASE
+ return false;
+ }
+ } else {
+ if (!bce_->emit1(JSOp::Dup2)) {
+ // [stack] OBJ KEY OBJ KEY
+ return false;
+ }
+ }
+ }
+
+ JSOp op;
+ if (isSuper()) {
+ op = JSOp::GetElemSuper;
+ } else {
+ op = JSOp::GetElem;
+ }
+ if (!bce_->emitElemOpBase(op, ShouldInstrument::Yes)) {
+ // [stack] # if Get
+ // [stack] ELEM
+ // [stack] # if Call
+ // [stack] THIS ELEM
+ // [stack] # if Inc/Dec/Assignment, with Super
+ // [stack] THIS KEY SUPERBASE ELEM
+ // [stack] # if Inc/Dec/Assignment, other
+ // [stack] OBJ KEY ELEM
+ return false;
+ }
+ if (isCall()) {
+ if (!bce_->emit1(JSOp::Swap)) {
+ // [stack] ELEM THIS
+ return false;
+ }
+ }
+
+#ifdef DEBUG
+ state_ = State::Get;
+#endif
+ return true;
+}
+
+bool ElemOpEmitter::prepareForRhs() {
+ MOZ_ASSERT(isSimpleAssignment() || isPropInit() || isCompoundAssignment());
+ MOZ_ASSERT_IF(isSimpleAssignment() || isPropInit(), state_ == State::Key);
+ MOZ_ASSERT_IF(isCompoundAssignment(), state_ == State::Get);
+
+ if (isSimpleAssignment() || isPropInit()) {
+ if (!emitPrivateGuard()) {
+ return false;
+ }
+ // For CompoundAssignment, SuperBase is already emitted by emitGet.
+ if (isSuper()) {
+ if (!bce_->emitSuperBase()) {
+ // [stack] THIS KEY SUPERBASE
+ return false;
+ }
+ }
+ }
+
+#ifdef DEBUG
+ state_ = State::Rhs;
+#endif
+ return true;
+}
+
+bool ElemOpEmitter::skipObjAndKeyAndRhs() {
+ MOZ_ASSERT(state_ == State::Start);
+ MOZ_ASSERT(isSimpleAssignment() || isPropInit());
+
+#ifdef DEBUG
+ state_ = State::Rhs;
+#endif
+ return true;
+}
+
+bool ElemOpEmitter::emitDelete() {
+ MOZ_ASSERT(state_ == State::Key);
+ MOZ_ASSERT(isDelete());
+ MOZ_ASSERT(!isPrivate());
+
+ if (isSuper()) {
+ if (!bce_->emit1(JSOp::ToPropertyKey)) {
+ // [stack] THIS KEY
+ return false;
+ }
+ if (!bce_->emitSuperBase()) {
+ // [stack] THIS KEY SUPERBASE
+ return false;
+ }
+
+ // Unconditionally throw when attempting to delete a super-reference.
+ if (!bce_->emit2(JSOp::ThrowMsg, uint8_t(ThrowMsgKind::CantDeleteSuper))) {
+ // [stack] THIS KEY SUPERBASE
+ return false;
+ }
+
+ // Another wrinkle: Balance the stack from the emitter's point of view.
+ // Execution will not reach here, as the last bytecode threw.
+ if (!bce_->emitPopN(2)) {
+ // [stack] THIS
+ return false;
+ }
+ } else {
+ MOZ_ASSERT(!isPrivate());
+ JSOp op = bce_->sc->strict() ? JSOp::StrictDelElem : JSOp::DelElem;
+ if (!bce_->emitElemOpBase(op)) {
+ // SUCCEEDED
+ return false;
+ }
+ }
+
+#ifdef DEBUG
+ state_ = State::Delete;
+#endif
+ return true;
+}
+
+bool ElemOpEmitter::emitAssignment() {
+ MOZ_ASSERT(isSimpleAssignment() || isPropInit() || isCompoundAssignment());
+ MOZ_ASSERT(state_ == State::Rhs);
+
+ MOZ_ASSERT_IF(isPropInit(), !isSuper());
+
+ JSOp setOp = isPropInit() ? JSOp::InitElem
+ : isSuper() ? bce_->sc->strict() ? JSOp::StrictSetElemSuper
+ : JSOp::SetElemSuper
+ : bce_->sc->strict() ? JSOp::StrictSetElem
+ : JSOp::SetElem;
+ if (!bce_->emitElemOpBase(setOp, ShouldInstrument::Yes)) {
+ // [stack] ELEM
+ return false;
+ }
+
+#ifdef DEBUG
+ state_ = State::Assignment;
+#endif
+ return true;
+}
+
+bool ElemOpEmitter::emitIncDec() {
+ MOZ_ASSERT(state_ == State::Key);
+ MOZ_ASSERT(isIncDec());
+
+ if (!emitGet()) {
+ // [stack] ... ELEM
+ return false;
+ }
+
+ MOZ_ASSERT(state_ == State::Get);
+
+ JSOp incOp = isInc() ? JSOp::Inc : JSOp::Dec;
+ if (!bce_->emit1(JSOp::ToNumeric)) {
+ // [stack] ... N
+ return false;
+ }
+ if (isPostIncDec()) {
+ // [stack] OBJ KEY SUPERBASE? N
+ if (!bce_->emit1(JSOp::Dup)) {
+ // [stack] ... N N
+ return false;
+ }
+ if (!bce_->emit2(JSOp::Unpick, 3 + isSuper())) {
+ // [stack] N OBJ KEY SUPERBASE? N
+ return false;
+ }
+ }
+ if (!bce_->emit1(incOp)) {
+ // [stack] ... N+1
+ return false;
+ }
+
+ JSOp setOp =
+ isSuper()
+ ? (bce_->sc->strict() ? JSOp::StrictSetElemSuper : JSOp::SetElemSuper)
+ : (bce_->sc->strict() ? JSOp::StrictSetElem : JSOp::SetElem);
+ if (!bce_->emitElemOpBase(setOp, ShouldInstrument::Yes)) {
+ // [stack] N? N+1
+ return false;
+ }
+ if (isPostIncDec()) {
+ if (!bce_->emit1(JSOp::Pop)) {
+ // [stack] N
+ return false;
+ }
+ }
+
+#ifdef DEBUG
+ state_ = State::IncDec;
+#endif
+ return true;
+}