summaryrefslogtreecommitdiffstats
path: root/js/src/frontend/BytecodeSection.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/frontend/BytecodeSection.h')
-rw-r--r--js/src/frontend/BytecodeSection.h418
1 files changed, 418 insertions, 0 deletions
diff --git a/js/src/frontend/BytecodeSection.h b/js/src/frontend/BytecodeSection.h
new file mode 100644
index 0000000000..d68561c783
--- /dev/null
+++ b/js/src/frontend/BytecodeSection.h
@@ -0,0 +1,418 @@
+/* -*- 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_BytecodeSection_h
+#define frontend_BytecodeSection_h
+
+#include "mozilla/Attributes.h" // MOZ_MUST_USE, MOZ_STACK_CLASS
+#include "mozilla/Maybe.h" // mozilla::Maybe
+#include "mozilla/Span.h" // mozilla::Span
+
+#include <stddef.h> // ptrdiff_t, size_t
+#include <stdint.h> // uint16_t, int32_t, uint32_t
+
+#include "jstypes.h" // JS_PUBLIC_API
+#include "NamespaceImports.h" // ValueVector
+
+#include "frontend/AbstractScopePtr.h" // AbstractScopePtr, ScopeIndex
+#include "frontend/BytecodeOffset.h" // BytecodeOffset
+#include "frontend/CompilationInfo.h" // CompilationStencil, CompilationGCOutput
+#include "frontend/JumpList.h" // JumpTarget
+#include "frontend/NameCollections.h" // AtomIndexMap, PooledMapPtr
+#include "frontend/ObjLiteral.h" // ObjLiteralStencil
+#include "frontend/ParseNode.h" // BigIntLiteral
+#include "frontend/SourceNotes.h" // SrcNote
+#include "frontend/Stencil.h" // Stencils
+#include "gc/Rooting.h" // JS::Rooted
+#include "js/GCVariant.h" // GCPolicy<mozilla::Variant>
+#include "js/GCVector.h" // GCVector
+#include "js/TypeDecls.h" // jsbytecode, JSContext
+#include "js/Value.h" // JS::Vector
+#include "js/Vector.h" // Vector
+#include "vm/Opcodes.h" // JSOpLength_JumpTarget
+#include "vm/SharedStencil.h" // TryNote, ScopeNote, GCThingIndex
+#include "vm/StencilEnums.h" // TryNoteKind
+
+namespace js {
+
+class Scope;
+
+namespace frontend {
+
+class FunctionBox;
+
+struct MOZ_STACK_CLASS GCThingList {
+ // The BCE accumulates TaggedScriptThingIndex items so use a vector type. We
+ // reserve some stack slots to avoid allocating for most small scripts.
+ using ScriptThingsStackVector = Vector<TaggedScriptThingIndex, 8>;
+
+ CompilationStencil& stencil;
+ CompilationState& compilationState;
+ ScriptThingsStackVector vector;
+
+ // Index of the first scope in the vector.
+ mozilla::Maybe<GCThingIndex> firstScopeIndex;
+
+ explicit GCThingList(JSContext* cx, CompilationStencil& stencil,
+ CompilationState& compilationState)
+ : stencil(stencil), compilationState(compilationState), vector(cx) {}
+
+ MOZ_MUST_USE bool append(const ParserAtom* atom, GCThingIndex* index) {
+ *index = GCThingIndex(vector.length());
+ atom->markUsedByStencil();
+ if (!vector.emplaceBack(atom->toIndex())) {
+ return false;
+ }
+ return true;
+ }
+ MOZ_MUST_USE bool append(ScopeIndex scope, GCThingIndex* index) {
+ *index = GCThingIndex(vector.length());
+ if (!vector.emplaceBack(scope)) {
+ return false;
+ }
+ if (!firstScopeIndex) {
+ firstScopeIndex.emplace(*index);
+ }
+ return true;
+ }
+ MOZ_MUST_USE bool append(BigIntLiteral* literal, GCThingIndex* index) {
+ *index = GCThingIndex(vector.length());
+ if (!vector.emplaceBack(literal->index())) {
+ return false;
+ }
+ return true;
+ }
+ MOZ_MUST_USE bool append(RegExpLiteral* literal, GCThingIndex* index) {
+ *index = GCThingIndex(vector.length());
+ if (!vector.emplaceBack(literal->index())) {
+ return false;
+ }
+ return true;
+ }
+ MOZ_MUST_USE bool append(ObjLiteralIndex objlit, GCThingIndex* index) {
+ *index = GCThingIndex(vector.length());
+ if (!vector.emplaceBack(objlit)) {
+ return false;
+ }
+ return true;
+ }
+ MOZ_MUST_USE bool append(FunctionBox* funbox, GCThingIndex* index);
+
+ MOZ_MUST_USE bool appendEmptyGlobalScope(GCThingIndex* index) {
+ *index = GCThingIndex(vector.length());
+ EmptyGlobalScopeType emptyGlobalScope;
+ if (!vector.emplaceBack(emptyGlobalScope)) {
+ return false;
+ }
+ if (!firstScopeIndex) {
+ firstScopeIndex.emplace(*index);
+ }
+ return true;
+ }
+
+ uint32_t length() const { return vector.length(); }
+
+ const ScriptThingsStackVector& objects() { return vector; }
+
+ AbstractScopePtr getScope(size_t index) const;
+
+ // Index of scope within CompilationStencil or Nothing is the scope is
+ // EmptyGlobalScopeType.
+ mozilla::Maybe<ScopeIndex> getScopeIndex(size_t index) const;
+
+ AbstractScopePtr firstScope() const {
+ MOZ_ASSERT(firstScopeIndex.isSome());
+ return getScope(*firstScopeIndex);
+ }
+};
+
+MOZ_MUST_USE bool EmitScriptThingsVector(
+ JSContext* cx, CompilationInput& input, BaseCompilationStencil& stencil,
+ CompilationGCOutput& gcOutput,
+ mozilla::Span<const TaggedScriptThingIndex> things,
+ mozilla::Span<JS::GCCellPtr> output);
+
+struct CGTryNoteList {
+ Vector<TryNote, 0> list;
+ explicit CGTryNoteList(JSContext* cx) : list(cx) {}
+
+ MOZ_MUST_USE bool append(TryNoteKind kind, uint32_t stackDepth,
+ BytecodeOffset start, BytecodeOffset end);
+ mozilla::Span<const TryNote> span() const {
+ return {list.begin(), list.length()};
+ }
+ size_t length() const { return list.length(); }
+};
+
+struct CGScopeNoteList {
+ Vector<ScopeNote, 0> list;
+ explicit CGScopeNoteList(JSContext* cx) : list(cx) {}
+
+ MOZ_MUST_USE bool append(GCThingIndex scopeIndex, BytecodeOffset offset,
+ uint32_t parent);
+ void recordEnd(uint32_t index, BytecodeOffset offset);
+ void recordEndFunctionBodyVar(uint32_t index);
+ mozilla::Span<const ScopeNote> span() const {
+ return {list.begin(), list.length()};
+ }
+ size_t length() const { return list.length(); }
+
+ private:
+ void recordEndImpl(uint32_t index, uint32_t offset);
+};
+
+struct CGResumeOffsetList {
+ Vector<uint32_t, 0> list;
+ explicit CGResumeOffsetList(JSContext* cx) : list(cx) {}
+
+ MOZ_MUST_USE bool append(uint32_t offset) { return list.append(offset); }
+ mozilla::Span<const uint32_t> span() const {
+ return {list.begin(), list.length()};
+ }
+ size_t length() const { return list.length(); }
+};
+
+static constexpr size_t MaxBytecodeLength = INT32_MAX;
+static constexpr size_t MaxSrcNotesLength = INT32_MAX;
+
+// Have a few inline elements, so as to avoid heap allocation for tiny
+// sequences. See bug 1390526.
+typedef Vector<jsbytecode, 64> BytecodeVector;
+typedef Vector<js::SrcNote, 64> SrcNotesVector;
+
+// Bytecode and all data directly associated with specific opcode/index inside
+// bytecode is stored in this class.
+class BytecodeSection {
+ public:
+ BytecodeSection(JSContext* cx, uint32_t lineNum, uint32_t column);
+
+ // ---- Bytecode ----
+
+ BytecodeVector& code() { return code_; }
+ const BytecodeVector& code() const { return code_; }
+
+ jsbytecode* code(BytecodeOffset offset) {
+ return code_.begin() + offset.value();
+ }
+ BytecodeOffset offset() const {
+ return BytecodeOffset(code_.end() - code_.begin());
+ }
+
+ // ---- Source notes ----
+
+ SrcNotesVector& notes() { return notes_; }
+ const SrcNotesVector& notes() const { return notes_; }
+
+ BytecodeOffset lastNoteOffset() const { return lastNoteOffset_; }
+ void setLastNoteOffset(BytecodeOffset offset) { lastNoteOffset_ = offset; }
+
+ // ---- Jump ----
+
+ BytecodeOffset lastTargetOffset() const { return lastTarget_.offset; }
+ void setLastTargetOffset(BytecodeOffset offset) {
+ lastTarget_.offset = offset;
+ }
+
+ // Check if the last emitted opcode is a jump target.
+ bool lastOpcodeIsJumpTarget() const {
+ return lastTarget_.offset.valid() &&
+ offset() - lastTarget_.offset ==
+ BytecodeOffsetDiff(JSOpLength_JumpTarget);
+ }
+
+ // JumpTarget should not be part of the emitted statement, as they can be
+ // aliased by multiple statements. If we included the jump target as part of
+ // the statement we might have issues where the enclosing statement might
+ // not contain all the opcodes of the enclosed statements.
+ BytecodeOffset lastNonJumpTargetOffset() const {
+ return lastOpcodeIsJumpTarget() ? lastTarget_.offset : offset();
+ }
+
+ // ---- Stack ----
+
+ int32_t stackDepth() const { return stackDepth_; }
+ void setStackDepth(int32_t depth) { stackDepth_ = depth; }
+
+ uint32_t maxStackDepth() const { return maxStackDepth_; }
+
+ void updateDepth(BytecodeOffset target);
+
+ // ---- Try notes ----
+
+ CGTryNoteList& tryNoteList() { return tryNoteList_; };
+ const CGTryNoteList& tryNoteList() const { return tryNoteList_; };
+
+ // ---- Scope ----
+
+ CGScopeNoteList& scopeNoteList() { return scopeNoteList_; };
+ const CGScopeNoteList& scopeNoteList() const { return scopeNoteList_; };
+
+ // ---- Generator ----
+
+ CGResumeOffsetList& resumeOffsetList() { return resumeOffsetList_; }
+ const CGResumeOffsetList& resumeOffsetList() const {
+ return resumeOffsetList_;
+ }
+
+ uint32_t numYields() const { return numYields_; }
+ void addNumYields() { numYields_++; }
+
+ // ---- Line and column ----
+
+ uint32_t currentLine() const { return currentLine_; }
+ uint32_t lastColumn() const { return lastColumn_; }
+ void setCurrentLine(uint32_t line, uint32_t sourceOffset) {
+ currentLine_ = line;
+ lastColumn_ = 0;
+ lastSourceOffset_ = sourceOffset;
+ }
+
+ void setLastColumn(uint32_t column, uint32_t offset) {
+ lastColumn_ = column;
+ lastSourceOffset_ = offset;
+ }
+
+ void updateSeparatorPosition() {
+ lastSeparatorCodeOffset_ = code().length();
+ lastSeparatorSourceOffset_ = lastSourceOffset_;
+ lastSeparatorLine_ = currentLine_;
+ lastSeparatorColumn_ = lastColumn_;
+ }
+
+ void updateSeparatorPositionIfPresent() {
+ if (lastSeparatorCodeOffset_ == code().length()) {
+ lastSeparatorSourceOffset_ = lastSourceOffset_;
+ lastSeparatorLine_ = currentLine_;
+ lastSeparatorColumn_ = lastColumn_;
+ }
+ }
+
+ bool isDuplicateLocation() const {
+ return lastSeparatorLine_ == currentLine_ &&
+ lastSeparatorColumn_ == lastColumn_;
+ }
+
+ bool atSeparator(uint32_t offset) const {
+ return lastSeparatorSourceOffset_ == offset;
+ }
+
+ // ---- JIT ----
+
+ uint32_t numICEntries() const { return numICEntries_; }
+ void incrementNumICEntries() {
+ MOZ_ASSERT(numICEntries_ != UINT32_MAX, "Shouldn't overflow");
+ numICEntries_++;
+ }
+ void setNumICEntries(uint32_t entries) { numICEntries_ = entries; }
+
+ private:
+ // ---- Bytecode ----
+
+ // Bytecode.
+ BytecodeVector code_;
+
+ // ---- Source notes ----
+
+ // Source notes
+ SrcNotesVector notes_;
+
+ // Code offset for last source note
+ BytecodeOffset lastNoteOffset_;
+
+ // ---- Jump ----
+
+ // Last jump target emitted.
+ JumpTarget lastTarget_;
+
+ // ---- Stack ----
+
+ // Maximum number of expression stack slots so far.
+ uint32_t maxStackDepth_ = 0;
+
+ // Current stack depth in script frame.
+ int32_t stackDepth_ = 0;
+
+ // ---- Try notes ----
+
+ // List of emitted try notes.
+ CGTryNoteList tryNoteList_;
+
+ // ---- Scope ----
+
+ // List of emitted block scope notes.
+ CGScopeNoteList scopeNoteList_;
+
+ // ---- Generator ----
+
+ // Certain ops (yield, await, gosub) have an entry in the script's
+ // resumeOffsets list. This can be used to map from the op's resumeIndex to
+ // the bytecode offset of the next pc. This indirection makes it easy to
+ // resume in the JIT (because BaselineScript stores a resumeIndex => native
+ // code array).
+ CGResumeOffsetList resumeOffsetList_;
+
+ // Number of yield instructions emitted. Does not include JSOp::Await.
+ uint32_t numYields_ = 0;
+
+ // ---- Line and column ----
+
+ // Line number for srcnotes.
+ //
+ // WARNING: If this becomes out of sync with already-emitted srcnotes,
+ // we can get undefined behavior.
+ uint32_t currentLine_;
+
+ // Zero-based column index on currentLine_ of last
+ // SrcNoteType::ColSpan-annotated opcode.
+ //
+ // WARNING: If this becomes out of sync with already-emitted srcnotes,
+ // we can get undefined behavior.
+ uint32_t lastColumn_ = 0;
+
+ // The last code unit used for srcnotes.
+ uint32_t lastSourceOffset_ = 0;
+
+ // The offset, line and column numbers of the last opcode for the
+ // breakpoint for step execution.
+ uint32_t lastSeparatorCodeOffset_ = 0;
+ uint32_t lastSeparatorSourceOffset_ = 0;
+ uint32_t lastSeparatorLine_ = 0;
+ uint32_t lastSeparatorColumn_ = 0;
+
+ // ---- JIT ----
+
+ // Number of ICEntries in the script. There's one ICEntry for each JOF_IC op
+ // and, if the script is a function, for |this| and each formal argument.
+ uint32_t numICEntries_ = 0;
+};
+
+// Data that is not directly associated with specific opcode/index inside
+// bytecode, but referred from bytecode is stored in this class.
+class PerScriptData {
+ public:
+ explicit PerScriptData(JSContext* cx, frontend::CompilationStencil& stencil,
+ frontend::CompilationState& compilationState);
+
+ MOZ_MUST_USE bool init(JSContext* cx);
+
+ GCThingList& gcThingList() { return gcThingList_; }
+ const GCThingList& gcThingList() const { return gcThingList_; }
+
+ PooledMapPtr<AtomIndexMap>& atomIndices() { return atomIndices_; }
+ const PooledMapPtr<AtomIndexMap>& atomIndices() const { return atomIndices_; }
+
+ private:
+ // List of emitted scopes/objects/bigints.
+ GCThingList gcThingList_;
+
+ // Map from atom to index.
+ PooledMapPtr<AtomIndexMap> atomIndices_;
+};
+
+} /* namespace frontend */
+} /* namespace js */
+
+#endif /* frontend_BytecodeSection_h */