summaryrefslogtreecommitdiffstats
path: root/js/src/frontend/IfEmitter.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--js/src/frontend/IfEmitter.h311
1 files changed, 311 insertions, 0 deletions
diff --git a/js/src/frontend/IfEmitter.h b/js/src/frontend/IfEmitter.h
new file mode 100644
index 0000000000..eaf4e2d8f4
--- /dev/null
+++ b/js/src/frontend/IfEmitter.h
@@ -0,0 +1,311 @@
+/* -*- 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_IfEmitter_h
+#define frontend_IfEmitter_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Maybe.h"
+
+#include <stdint.h>
+
+#include "frontend/JumpList.h"
+#include "frontend/TDZCheckCache.h"
+
+namespace js {
+namespace frontend {
+
+struct BytecodeEmitter;
+
+class MOZ_STACK_CLASS BranchEmitterBase {
+ protected:
+ BytecodeEmitter* bce_;
+
+ // Jump around the then clause, to the beginning of the else clause.
+ JumpList jumpAroundThen_;
+
+ // Jump around the else clause, to the end of the entire branch.
+ JumpList jumpsAroundElse_;
+
+ // The stack depth before emitting the then block.
+ // Used for restoring stack depth before emitting the else block.
+ // Also used for assertion to make sure then and else blocks pushed the
+ // same number of values.
+ int32_t thenDepth_ = 0;
+
+ enum class ConditionKind { Positive, Negative };
+
+ // Whether the then-clause, the else-clause, or else-if condition may
+ // contain declaration or access to lexical variables, which means they
+ // should have their own TDZCheckCache. Basically TDZCheckCache should be
+ // created for each basic block, which then-clause, else-clause, and
+ // else-if condition are, but for internally used branches which are
+ // known not to touch lexical variables we can skip creating TDZCheckCache
+ // for them.
+ //
+ // See the comment for TDZCheckCache class for more details.
+ enum class LexicalKind {
+ // For syntactic branches (if, if-else, and conditional expression),
+ // which basically may contain declaration or accesses to lexical
+ // variables inside then-clause, else-clause, and else-if condition.
+ MayContainLexicalAccessInBranch,
+
+ // For internally used branches which don't touch lexical variables
+ // inside then-clause, else-clause, nor else-if condition.
+ NoLexicalAccessInBranch
+ };
+ LexicalKind lexicalKind_;
+
+ mozilla::Maybe<TDZCheckCache> tdzCache_;
+
+#ifdef DEBUG
+ // The number of values pushed in the then and else blocks.
+ int32_t pushed_ = 0;
+ bool calculatedPushed_ = false;
+#endif
+
+ protected:
+ BranchEmitterBase(BytecodeEmitter* bce, LexicalKind lexicalKind);
+
+ [[nodiscard]] bool emitThenInternal(ConditionKind conditionKind);
+ void calculateOrCheckPushed();
+ [[nodiscard]] bool emitElseInternal();
+ [[nodiscard]] bool emitEndInternal();
+
+ public:
+#ifdef DEBUG
+ // Returns the number of values pushed onto the value stack inside
+ // `then_block` and `else_block`.
+ // Can be used in assertion after emitting if-then-else.
+ int32_t pushed() const { return pushed_; }
+
+ // Returns the number of values popped onto the value stack inside
+ // `then_block` and `else_block`.
+ // Can be used in assertion after emitting if-then-else.
+ int32_t popped() const { return -pushed_; }
+#endif
+};
+
+// Class for emitting bytecode for blocks like if-then-else.
+//
+// This class can be used to emit single if-then-else block, or cascading
+// else-if blocks.
+//
+// Usage: (check for the return value is omitted for simplicity)
+//
+// `if (cond) then_block`
+// IfEmitter ifThen(this);
+// ifThen.emitIf(Some(offset_of_if));
+// emit(cond);
+// ifThen.emitThen();
+// emit(then_block);
+// ifThen.emitEnd();
+//
+// `if (!cond) then_block`
+// IfEmitter ifThen(this);
+// ifThen.emitIf(Some(offset_of_if));
+// emit(cond);
+// ifThen.emitThen(IfEmitter::ConditionKind::Negative);
+// emit(then_block);
+// ifThen.emitEnd();
+//
+// `if (cond) then_block else else_block`
+// IfEmitter ifThenElse(this);
+// ifThen.emitIf(Some(offset_of_if));
+// emit(cond);
+// ifThenElse.emitThenElse();
+// emit(then_block);
+// ifThenElse.emitElse();
+// emit(else_block);
+// ifThenElse.emitEnd();
+//
+// `if (c1) b1 else if (c2) b2 else if (c3) b3 else b4`
+// IfEmitter ifThenElse(this);
+// ifThen.emitIf(Some(offset_of_if));
+// emit(c1);
+// ifThenElse.emitThenElse();
+// emit(b1);
+// ifThenElse.emitElseIf(Some(offset_of_if));
+// emit(c2);
+// ifThenElse.emitThenElse();
+// emit(b2);
+// ifThenElse.emitElseIf(Some(offset_of_if));
+// emit(c3);
+// ifThenElse.emitThenElse();
+// emit(b3);
+// ifThenElse.emitElse();
+// emit(b4);
+// ifThenElse.emitEnd();
+//
+class MOZ_STACK_CLASS IfEmitter : public BranchEmitterBase {
+ public:
+ using ConditionKind = BranchEmitterBase::ConditionKind;
+
+ protected:
+#ifdef DEBUG
+ // The state of this emitter.
+ //
+ // +-------+ emitIf +----+
+ // | Start |------->| If |-+
+ // +-------+ +----+ |
+ // |
+ // +--------------------+
+ // |
+ // v emitThen +------+ emitEnd +-----+
+ // +->+--------->| Then |---------------------------->+-------->| End |
+ // ^ | +------+ ^ +-----+
+ // | | |
+ // | | |
+ // | | |
+ // | | emitThenElse +----------+ emitElse +------+ |
+ // | +------------->| ThenElse |-+--------->| Else |-+
+ // | +----------+ | +------+
+ // | |
+ // | | emitElseIf +--------+
+ // | +----------->| ElseIf |-+
+ // | +--------+ |
+ // | |
+ // +------------------------------------------------------+
+ enum class State {
+ // The initial state.
+ Start,
+
+ // After calling emitIf.
+ If,
+
+ // After calling emitThen.
+ Then,
+
+ // After calling emitThenElse.
+ ThenElse,
+
+ // After calling emitElse.
+ Else,
+
+ // After calling emitElseIf.
+ ElseIf,
+
+ // After calling emitEnd.
+ End
+ };
+ State state_ = State::Start;
+#endif
+
+ protected:
+ // For InternalIfEmitter.
+ IfEmitter(BytecodeEmitter* bce, LexicalKind lexicalKind);
+
+ public:
+ explicit IfEmitter(BytecodeEmitter* bce);
+
+ // `ifPos` is the offset in the source code for the character below:
+ //
+ // if ( cond ) { ... } else if ( cond2 ) { ... }
+ // ^ ^
+ // | |
+ // | ifPos for emitElseIf
+ // |
+ // ifPos for emitIf
+ //
+ // Can be Nothing() if not available.
+ [[nodiscard]] bool emitIf(const mozilla::Maybe<uint32_t>& ifPos);
+
+ [[nodiscard]] bool emitThen(
+ ConditionKind conditionKind = ConditionKind::Positive);
+ [[nodiscard]] bool emitThenElse(
+ ConditionKind conditionKind = ConditionKind::Positive);
+
+ [[nodiscard]] bool emitElseIf(const mozilla::Maybe<uint32_t>& ifPos);
+ [[nodiscard]] bool emitElse();
+
+ [[nodiscard]] bool emitEnd();
+};
+
+// Class for emitting bytecode for blocks like if-then-else which doesn't touch
+// lexical variables.
+//
+// See the comments above NoLexicalAccessInBranch for more details when to use
+// this instead of IfEmitter.
+// Compared to IfEmitter, this class doesn't have emitIf method, given that
+// it doesn't have syntactic `if`, and also the `cond` value can be already
+// on the stack.
+//
+// Usage: (check for the return value is omitted for simplicity)
+//
+// `if (cond) then_block else else_block` (effectively)
+// emit(cond);
+// InternalIfEmitter ifThenElse(this);
+// ifThenElse.emitThenElse();
+// emit(then_block);
+// ifThenElse.emitElse();
+// emit(else_block);
+// ifThenElse.emitEnd();
+//
+class MOZ_STACK_CLASS InternalIfEmitter : public IfEmitter {
+ public:
+ explicit InternalIfEmitter(BytecodeEmitter* bce);
+};
+
+// Class for emitting bytecode for conditional expression.
+//
+// Usage: (check for the return value is omitted for simplicity)
+//
+// `cond ? then_expr : else_expr`
+// CondEmitter condElse(this);
+// condElse.emitCond();
+// emit(cond);
+// condElse.emitThenElse();
+// emit(then_expr);
+// condElse.emitElse();
+// emit(else_expr);
+// condElse.emitEnd();
+//
+class MOZ_STACK_CLASS CondEmitter : public BranchEmitterBase {
+#ifdef DEBUG
+ // The state of this emitter.
+ //
+ // +-------+ emitCond +------+ emitThenElse +----------+
+ // | Start |--------->| Cond |------------->| ThenElse |-+
+ // +-------+ +------+ +----------+ |
+ // |
+ // +-----------------+
+ // |
+ // | emitElse +------+ emitEnd +-----+
+ // +--------->| Else |-------->| End |
+ // +------+ +-----+
+ enum class State {
+ // The initial state.
+ Start,
+
+ // After calling emitCond.
+ Cond,
+
+ // After calling emitThenElse.
+ ThenElse,
+
+ // After calling emitElse.
+ Else,
+
+ // After calling emitEnd.
+ End
+ };
+ State state_ = State::Start;
+#endif
+
+ public:
+ explicit CondEmitter(BytecodeEmitter* bce);
+
+ [[nodiscard]] bool emitCond();
+ [[nodiscard]] bool emitThenElse(
+ ConditionKind conditionKind = ConditionKind::Positive);
+ [[nodiscard]] bool emitElse();
+ [[nodiscard]] bool emitEnd();
+};
+
+} /* namespace frontend */
+} /* namespace js */
+
+#endif /* frontend_IfEmitter_h */