summaryrefslogtreecommitdiffstats
path: root/js/src/frontend/PrivateOpEmitter.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--js/src/frontend/PrivateOpEmitter.h231
1 files changed, 231 insertions, 0 deletions
diff --git a/js/src/frontend/PrivateOpEmitter.h b/js/src/frontend/PrivateOpEmitter.h
new file mode 100644
index 0000000000..558541b05c
--- /dev/null
+++ b/js/src/frontend/PrivateOpEmitter.h
@@ -0,0 +1,231 @@
+/* -*- 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/. */
+
+#ifndef frontend_PrivateOpEmitter_h
+#define frontend_PrivateOpEmitter_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Maybe.h"
+
+#include <stddef.h>
+
+#include "frontend/NameAnalysisTypes.h" // NameLocation
+#include "frontend/ParserAtom.h" // TaggedParserAtomIndex
+
+namespace js {
+namespace frontend {
+
+struct BytecodeEmitter;
+enum class ValueUsage;
+
+// Class for emitting bytecode for operations on private members of objects.
+//
+// Usage is similar to PropOpEmitter, but the name of the private member must
+// be passed to the constructor; prepare*() methods aren't necessary; and
+// `delete obj.#member` and `super.#member` aren't supported because both are
+// SyntaxErrors.
+//
+// Usage: (error checking is omitted for simplicity)
+//
+// `obj.#member;`
+// PrivateOpEmitter xoe(this,
+// privateName,
+// PrivateOpEmitter::Kind::Get);
+// emit(obj);
+// xoe.emitReference();
+// xoe.emitGet();
+//
+// `obj.#member();`
+// PrivateOpEmitter xoe(this,
+// privateName,
+// PrivateOpEmitter::Kind::Call);
+// emit(obj);
+// xoe.emitReference();
+// xoe.emitGet();
+// emit_call_here();
+//
+// `new obj.#member();`
+// The same, but use PrivateOpEmitter::Kind::Get.
+//
+// `obj.#field++;`
+// PrivateOpEmitter xoe(this,
+// privateName,
+// PrivateOpEmitter::Kind::PostIncrement);
+// emit(obj);
+// xoe.emitReference();
+// xoe.emitIncDec();
+//
+// `obj.#field = value;`
+// PrivateOpEmitter xoe(this,
+// privateName,
+// PrivateOpEmitter::Kind::SimpleAssignment);
+// emit(obj);
+// xoe.emitReference();
+// emit(value);
+// xoe.emitAssignment();
+//
+// `obj.#field += value;`
+// PrivateOpEmitter xoe(this,
+// privateName,
+// PrivateOpEmitter::Kind::CompoundAssignment);
+// emit(obj);
+// xoe.emitReference();
+// emit(JSOp::Dup2);
+// xoe.emitGet();
+// emit(value);
+// emit_add_op_here();
+// xoe.emitAssignment();
+//
+class MOZ_STACK_CLASS PrivateOpEmitter {
+ public:
+ enum class Kind {
+ Get,
+ Call,
+ Delete,
+ PostIncrement,
+ PreIncrement,
+ PostDecrement,
+ PreDecrement,
+ SimpleAssignment,
+ PropInit,
+ CompoundAssignment,
+ ErgonomicBrandCheck,
+ };
+
+ private:
+ BytecodeEmitter* bce_;
+
+ Kind kind_;
+
+ // Name of the private member, e.g. "#field".
+ TaggedParserAtomIndex name_;
+
+ // Location of the slot containing the private name symbol; or, for a
+ // non-static private method, the slot containing the method.
+ mozilla::Maybe<NameLocation> loc_;
+
+ // For non-static private method accesses, the location of the relevant
+ // `.privateBrand` binding. Otherwise, `Nothing`.
+ mozilla::Maybe<NameLocation> brandLoc_{};
+
+#ifdef DEBUG
+ // The state of this emitter.
+ //
+ // emitReference
+ // +-------+ skipReference +-----------+
+ // | Start |---------------->| Reference |
+ // +-------+ +-----+-----+
+ // |
+ // +---------------------------+
+ // |
+ // |
+ // | [Get]
+ // | emitGet
+ // | [Call] [Get] [CompoundAssignment]
+ // | emitGetForCallOrNew +-----+ emitAssignment
+ // +---------------------->| Get |-------------------+
+ // | +-----+ |
+ // | [PostIncrement] |
+ // | [PreIncrement] |
+ // | [PostDecrement] |
+ // | [PreDecrement] |
+ // | emitIncDec |
+ // +------------------------------------------------>+
+ // | |
+ // | [SimpleAssignment] |
+ // | [PropInit] V
+ // | emitAssignment +------------+
+ // +------------------------------------------>| Assignment |
+ // +------------+
+ enum class State {
+ // The initial state.
+ Start,
+
+ // After calling emitReference or skipReference.
+ Reference,
+
+ // After calling emitGet.
+ Get,
+
+ // After calling emitAssignment or emitIncDec.
+ Assignment,
+ };
+ State state_ = State::Start;
+#endif
+
+ public:
+ PrivateOpEmitter(BytecodeEmitter* bce, Kind kind, TaggedParserAtomIndex name);
+
+ private:
+ [[nodiscard]] bool isCall() const { return kind_ == Kind::Call; }
+
+ [[nodiscard]] bool isSimpleAssignment() const {
+ return kind_ == Kind::SimpleAssignment;
+ }
+
+ [[nodiscard]] bool isFieldInit() const { return kind_ == Kind::PropInit; }
+
+ [[nodiscard]] bool isBrandCheck() const {
+ return kind_ == Kind::ErgonomicBrandCheck;
+ }
+
+ [[nodiscard]] bool isCompoundAssignment() const {
+ return kind_ == Kind::CompoundAssignment;
+ }
+
+ [[nodiscard]] bool isIncDec() const {
+ return isPostIncDec() || isPreIncDec();
+ }
+
+ [[nodiscard]] bool isPostIncDec() const {
+ return kind_ == Kind::PostIncrement || kind_ == Kind::PostDecrement;
+ }
+
+ [[nodiscard]] bool isPreIncDec() const {
+ return kind_ == Kind::PreIncrement || kind_ == Kind::PreDecrement;
+ }
+
+ [[nodiscard]] bool isInc() const {
+ return kind_ == Kind::PostIncrement || kind_ == Kind::PreIncrement;
+ }
+
+ [[nodiscard]] bool init();
+
+ // Emit a GetAliasedLexical or similar instruction.
+ [[nodiscard]] bool emitLoad(TaggedParserAtomIndex name,
+ const NameLocation& loc);
+
+ [[nodiscard]] bool emitLoadPrivateBrand();
+
+ public:
+ // Emit bytecode to check for the presence/absence of a private field/brand.
+ //
+ // Given OBJ KEY on the stack, where KEY is a private name symbol, the
+ // emitted code will throw if OBJ does not have the given KEY.
+ //
+ // If `isFieldInit()`, the check is reversed: the code will throw if OBJ
+ // already has the KEY.
+ //
+ // If `isBrandCheck()`, the check verifies RHS is an object (throwing if not).
+ //
+ // The bytecode leaves OBJ KEY BOOL on the stack. Caller is responsible for
+ // consuming or popping it.
+ [[nodiscard]] bool emitBrandCheck();
+
+ [[nodiscard]] bool emitReference();
+ [[nodiscard]] bool skipReference();
+ [[nodiscard]] bool emitGet();
+ [[nodiscard]] bool emitGetForCallOrNew();
+ [[nodiscard]] bool emitAssignment();
+ [[nodiscard]] bool emitIncDec(ValueUsage valueUsage);
+
+ [[nodiscard]] size_t numReferenceSlots() { return 2; }
+};
+
+} /* namespace frontend */
+} /* namespace js */
+
+#endif /* frontend_PrivateOpEmitter_h */