786 lines
32 KiB
C++
786 lines
32 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
* 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/. */
|
|
|
|
/* JS bytecode generation. */
|
|
|
|
#ifndef frontend_BytecodeEmitter_h
|
|
#define frontend_BytecodeEmitter_h
|
|
|
|
#include "jscntxt.h"
|
|
#include "jsopcode.h"
|
|
#include "jsscript.h"
|
|
|
|
#include "ds/InlineTable.h"
|
|
#include "frontend/Parser.h"
|
|
#include "frontend/SharedContext.h"
|
|
#include "frontend/SourceNotes.h"
|
|
#include "vm/Interpreter.h"
|
|
|
|
namespace js {
|
|
namespace frontend {
|
|
|
|
class FullParseHandler;
|
|
class ObjectBox;
|
|
class ParseNode;
|
|
template <typename ParseHandler> class Parser;
|
|
class SharedContext;
|
|
class TokenStream;
|
|
|
|
class CGConstList {
|
|
Vector<Value> list;
|
|
public:
|
|
explicit CGConstList(ExclusiveContext* cx) : list(cx) {}
|
|
MOZ_MUST_USE bool append(const Value& v) {
|
|
MOZ_ASSERT_IF(v.isString(), v.toString()->isAtom());
|
|
return list.append(v);
|
|
}
|
|
size_t length() const { return list.length(); }
|
|
void finish(ConstArray* array);
|
|
};
|
|
|
|
struct CGObjectList {
|
|
uint32_t length; /* number of emitted so far objects */
|
|
ObjectBox* lastbox; /* last emitted object */
|
|
|
|
CGObjectList() : length(0), lastbox(nullptr) {}
|
|
|
|
unsigned add(ObjectBox* objbox);
|
|
unsigned indexOf(JSObject* obj);
|
|
void finish(ObjectArray* array);
|
|
ObjectBox* find(uint32_t index);
|
|
};
|
|
|
|
struct MOZ_STACK_CLASS CGScopeList {
|
|
Rooted<GCVector<Scope*>> vector;
|
|
|
|
explicit CGScopeList(ExclusiveContext* cx)
|
|
: vector(cx, GCVector<Scope*>(cx))
|
|
{ }
|
|
|
|
bool append(Scope* scope) { return vector.append(scope); }
|
|
uint32_t length() const { return vector.length(); }
|
|
void finish(ScopeArray* array);
|
|
};
|
|
|
|
struct CGTryNoteList {
|
|
Vector<JSTryNote> list;
|
|
explicit CGTryNoteList(ExclusiveContext* cx) : list(cx) {}
|
|
|
|
MOZ_MUST_USE bool append(JSTryNoteKind kind, uint32_t stackDepth, size_t start, size_t end);
|
|
size_t length() const { return list.length(); }
|
|
void finish(TryNoteArray* array);
|
|
};
|
|
|
|
struct CGScopeNote : public ScopeNote
|
|
{
|
|
// The end offset. Used to compute the length; may need adjusting first if
|
|
// in the prologue.
|
|
uint32_t end;
|
|
|
|
// Is the start offset in the prologue?
|
|
bool startInPrologue;
|
|
|
|
// Is the end offset in the prologue?
|
|
bool endInPrologue;
|
|
};
|
|
|
|
struct CGScopeNoteList {
|
|
Vector<CGScopeNote> list;
|
|
explicit CGScopeNoteList(ExclusiveContext* cx) : list(cx) {}
|
|
|
|
MOZ_MUST_USE bool append(uint32_t scopeIndex, uint32_t offset, bool inPrologue,
|
|
uint32_t parent);
|
|
void recordEnd(uint32_t index, uint32_t offset, bool inPrologue);
|
|
size_t length() const { return list.length(); }
|
|
void finish(ScopeNoteArray* array, uint32_t prologueLength);
|
|
};
|
|
|
|
struct CGYieldOffsetList {
|
|
Vector<uint32_t> list;
|
|
uint32_t numYields;
|
|
uint32_t numAwaits;
|
|
explicit CGYieldOffsetList(ExclusiveContext* cx) : list(cx), numYields(0), numAwaits(0) {}
|
|
|
|
MOZ_MUST_USE bool append(uint32_t offset) { return list.append(offset); }
|
|
size_t length() const { return list.length(); }
|
|
void finish(YieldOffsetArray& array, uint32_t prologueLength);
|
|
};
|
|
|
|
// Use zero inline elements because these go on the stack and affect how many
|
|
// nested functions are possible.
|
|
typedef Vector<jsbytecode, 0> BytecodeVector;
|
|
typedef Vector<jssrcnote, 0> SrcNotesVector;
|
|
|
|
// Linked list of jump instructions that need to be patched. The linked list is
|
|
// stored in the bytes of the incomplete bytecode that will be patched, so no
|
|
// extra memory is needed, and patching the instructions destroys the list.
|
|
//
|
|
// Example:
|
|
//
|
|
// JumpList brList;
|
|
// if (!emitJump(JSOP_IFEQ, &brList))
|
|
// return false;
|
|
// ...
|
|
// JumpTarget label;
|
|
// if (!emitJumpTarget(&label))
|
|
// return false;
|
|
// ...
|
|
// if (!emitJump(JSOP_GOTO, &brList))
|
|
// return false;
|
|
// ...
|
|
// patchJumpsToTarget(brList, label);
|
|
//
|
|
// +-> -1
|
|
// |
|
|
// |
|
|
// ifeq .. <+ + +-+ ifeq ..
|
|
// .. | | ..
|
|
// label: | +-> label:
|
|
// jumptarget | | jumptarget
|
|
// .. | | ..
|
|
// goto .. <+ + +-+ goto .. <+
|
|
// | |
|
|
// | |
|
|
// + +
|
|
// brList brList
|
|
//
|
|
// | ^
|
|
// +------- patchJumpsToTarget -------+
|
|
//
|
|
|
|
// Offset of a jump target instruction, used for patching jump instructions.
|
|
struct JumpTarget {
|
|
ptrdiff_t offset;
|
|
};
|
|
|
|
struct JumpList {
|
|
// -1 is used to mark the end of jump lists.
|
|
JumpList() : offset(-1) {}
|
|
ptrdiff_t offset;
|
|
|
|
// Add a jump instruction to the list.
|
|
void push(jsbytecode* code, ptrdiff_t jumpOffset);
|
|
|
|
// Patch all jump instructions in this list to jump to `target`. This
|
|
// clobbers the list.
|
|
void patchAll(jsbytecode* code, JumpTarget target);
|
|
};
|
|
|
|
struct MOZ_STACK_CLASS BytecodeEmitter
|
|
{
|
|
class TDZCheckCache;
|
|
class NestableControl;
|
|
class EmitterScope;
|
|
|
|
SharedContext* const sc; /* context shared between parsing and bytecode generation */
|
|
|
|
ExclusiveContext* const cx;
|
|
|
|
BytecodeEmitter* const parent; /* enclosing function or global context */
|
|
|
|
Rooted<JSScript*> script; /* the JSScript we're ultimately producing */
|
|
|
|
Rooted<LazyScript*> lazyScript; /* the lazy script if mode is LazyFunction,
|
|
nullptr otherwise. */
|
|
|
|
struct EmitSection {
|
|
BytecodeVector code; /* bytecode */
|
|
SrcNotesVector notes; /* source notes, see below */
|
|
ptrdiff_t lastNoteOffset; /* code offset for last source note */
|
|
uint32_t currentLine; /* line number for tree-based srcnote gen */
|
|
uint32_t lastColumn; /* zero-based column index on currentLine of
|
|
last SRC_COLSPAN-annotated opcode */
|
|
JumpTarget lastTarget; // Last jump target emitted.
|
|
|
|
EmitSection(ExclusiveContext* cx, uint32_t lineNum)
|
|
: code(cx), notes(cx), lastNoteOffset(0), currentLine(lineNum), lastColumn(0),
|
|
lastTarget{ -1 - ptrdiff_t(JSOP_JUMPTARGET_LENGTH) }
|
|
{}
|
|
};
|
|
EmitSection prologue, main, *current;
|
|
|
|
Parser<FullParseHandler>* const parser;
|
|
|
|
PooledMapPtr<AtomIndexMap> atomIndices; /* literals indexed for mapping */
|
|
unsigned firstLine; /* first line, for JSScript::initFromEmitter */
|
|
|
|
uint32_t maxFixedSlots; /* maximum number of fixed frame slots so far */
|
|
uint32_t maxStackDepth; /* maximum number of expression stack slots so far */
|
|
|
|
int32_t stackDepth; /* current stack depth in script frame */
|
|
|
|
uint32_t arrayCompDepth; /* stack depth of array in comprehension */
|
|
|
|
unsigned emitLevel; /* emitTree recursion level */
|
|
|
|
uint32_t bodyScopeIndex; /* index into scopeList of the body scope */
|
|
|
|
EmitterScope* varEmitterScope;
|
|
NestableControl* innermostNestableControl;
|
|
EmitterScope* innermostEmitterScope;
|
|
TDZCheckCache* innermostTDZCheckCache;
|
|
|
|
CGConstList constList; /* constants to be included with the script */
|
|
CGObjectList objectList; /* list of emitted objects */
|
|
CGScopeList scopeList; /* list of emitted scopes */
|
|
CGTryNoteList tryNoteList; /* list of emitted try notes */
|
|
CGScopeNoteList scopeNoteList; /* list of emitted block scope notes */
|
|
|
|
/*
|
|
* For each yield op, map the yield index (stored as bytecode operand) to
|
|
* the offset of the next op.
|
|
*/
|
|
CGYieldOffsetList yieldOffsetList;
|
|
|
|
uint16_t typesetCount; /* Number of JOF_TYPESET opcodes generated */
|
|
|
|
bool hasSingletons:1; /* script contains singleton initializer JSOP_OBJECT */
|
|
|
|
bool hasTryFinally:1; /* script contains finally block */
|
|
|
|
bool emittingRunOnceLambda:1; /* true while emitting a lambda which is only
|
|
expected to run once. */
|
|
|
|
bool isRunOnceLambda();
|
|
|
|
enum EmitterMode {
|
|
Normal,
|
|
|
|
/*
|
|
* Emit JSOP_GETINTRINSIC instead of JSOP_GETNAME and assert that
|
|
* JSOP_GETNAME and JSOP_*GNAME don't ever get emitted. See the comment
|
|
* for the field |selfHostingMode| in Parser.h for details.
|
|
*/
|
|
SelfHosting,
|
|
|
|
/*
|
|
* Check the static scope chain of the root function for resolving free
|
|
* variable accesses in the script.
|
|
*/
|
|
LazyFunction
|
|
};
|
|
|
|
const EmitterMode emitterMode;
|
|
|
|
// The end location of a function body that is being emitted.
|
|
uint32_t functionBodyEndPos;
|
|
// Whether functionBodyEndPos was set.
|
|
bool functionBodyEndPosSet;
|
|
|
|
/*
|
|
* Note that BytecodeEmitters are magic: they own the arena "top-of-stack"
|
|
* space above their tempMark points. This means that you cannot alloc from
|
|
* tempLifoAlloc and save the pointer beyond the next BytecodeEmitter
|
|
* destruction.
|
|
*/
|
|
BytecodeEmitter(BytecodeEmitter* parent, Parser<FullParseHandler>* parser, SharedContext* sc,
|
|
HandleScript script, Handle<LazyScript*> lazyScript, uint32_t lineNum,
|
|
EmitterMode emitterMode = Normal);
|
|
|
|
// An alternate constructor that uses a TokenPos for the starting
|
|
// line and that sets functionBodyEndPos as well.
|
|
BytecodeEmitter(BytecodeEmitter* parent, Parser<FullParseHandler>* parser, SharedContext* sc,
|
|
HandleScript script, Handle<LazyScript*> lazyScript,
|
|
TokenPos bodyPosition, EmitterMode emitterMode = Normal);
|
|
|
|
MOZ_MUST_USE bool init();
|
|
|
|
template <typename Predicate /* (NestableControl*) -> bool */>
|
|
NestableControl* findInnermostNestableControl(Predicate predicate) const;
|
|
|
|
template <typename T>
|
|
T* findInnermostNestableControl() const;
|
|
|
|
template <typename T, typename Predicate /* (T*) -> bool */>
|
|
T* findInnermostNestableControl(Predicate predicate) const;
|
|
|
|
NameLocation lookupName(JSAtom* name);
|
|
|
|
// To implement Annex B and the formal parameter defaults scope semantics
|
|
// requires accessing names that would otherwise be shadowed. This method
|
|
// returns the access location of a name that is known to be bound in a
|
|
// target scope.
|
|
mozilla::Maybe<NameLocation> locationOfNameBoundInScope(JSAtom* name, EmitterScope* target);
|
|
|
|
// Get the location of a name known to be bound in the function scope,
|
|
// starting at the source scope.
|
|
mozilla::Maybe<NameLocation> locationOfNameBoundInFunctionScope(JSAtom* name,
|
|
EmitterScope* source);
|
|
|
|
mozilla::Maybe<NameLocation> locationOfNameBoundInFunctionScope(JSAtom* name) {
|
|
return locationOfNameBoundInFunctionScope(name, innermostEmitterScope);
|
|
}
|
|
|
|
void setVarEmitterScope(EmitterScope* emitterScope) {
|
|
MOZ_ASSERT(emitterScope);
|
|
MOZ_ASSERT(!varEmitterScope);
|
|
varEmitterScope = emitterScope;
|
|
}
|
|
|
|
Scope* bodyScope() const { return scopeList.vector[bodyScopeIndex]; }
|
|
Scope* outermostScope() const { return scopeList.vector[0]; }
|
|
Scope* innermostScope() const;
|
|
|
|
MOZ_ALWAYS_INLINE
|
|
MOZ_MUST_USE bool makeAtomIndex(JSAtom* atom, uint32_t* indexp) {
|
|
MOZ_ASSERT(atomIndices);
|
|
AtomIndexMap::AddPtr p = atomIndices->lookupForAdd(atom);
|
|
if (p) {
|
|
*indexp = p->value();
|
|
return true;
|
|
}
|
|
|
|
uint32_t index = atomIndices->count();
|
|
if (!atomIndices->add(p, atom, index))
|
|
return false;
|
|
|
|
*indexp = index;
|
|
return true;
|
|
}
|
|
|
|
bool isInLoop();
|
|
MOZ_MUST_USE bool checkSingletonContext();
|
|
|
|
// Check whether our function is in a run-once context (a toplevel
|
|
// run-one script or a run-once lambda).
|
|
MOZ_MUST_USE bool checkRunOnceContext();
|
|
|
|
bool needsImplicitThis();
|
|
|
|
MOZ_MUST_USE bool maybeSetDisplayURL();
|
|
MOZ_MUST_USE bool maybeSetSourceMap();
|
|
void tellDebuggerAboutCompiledScript(ExclusiveContext* cx);
|
|
|
|
inline TokenStream* tokenStream();
|
|
|
|
BytecodeVector& code() const { return current->code; }
|
|
jsbytecode* code(ptrdiff_t offset) const { return current->code.begin() + offset; }
|
|
ptrdiff_t offset() const { return current->code.end() - current->code.begin(); }
|
|
ptrdiff_t prologueOffset() const { return prologue.code.end() - prologue.code.begin(); }
|
|
void switchToMain() { current = &main; }
|
|
void switchToPrologue() { current = &prologue; }
|
|
bool inPrologue() const { return current == &prologue; }
|
|
|
|
SrcNotesVector& notes() const { return current->notes; }
|
|
ptrdiff_t lastNoteOffset() const { return current->lastNoteOffset; }
|
|
unsigned currentLine() const { return current->currentLine; }
|
|
unsigned lastColumn() const { return current->lastColumn; }
|
|
|
|
// Check if the last emitted opcode is a jump target.
|
|
bool lastOpcodeIsJumpTarget() const {
|
|
return offset() - current->lastTarget.offset == ptrdiff_t(JSOP_JUMPTARGET_LENGTH);
|
|
}
|
|
|
|
// 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.
|
|
ptrdiff_t lastNonJumpTargetOffset() const {
|
|
return lastOpcodeIsJumpTarget() ? current->lastTarget.offset : offset();
|
|
}
|
|
|
|
void setFunctionBodyEndPos(TokenPos pos) {
|
|
functionBodyEndPos = pos.end;
|
|
functionBodyEndPosSet = true;
|
|
}
|
|
|
|
bool reportError(ParseNode* pn, unsigned errorNumber, ...);
|
|
bool reportExtraWarning(ParseNode* pn, unsigned errorNumber, ...);
|
|
bool reportStrictModeError(ParseNode* pn, unsigned errorNumber, ...);
|
|
|
|
// If pn contains a useful expression, return true with *answer set to true.
|
|
// If pn contains a useless expression, return true with *answer set to
|
|
// false. Return false on error.
|
|
//
|
|
// The caller should initialize *answer to false and invoke this function on
|
|
// an expression statement or similar subtree to decide whether the tree
|
|
// could produce code that has any side effects. For an expression
|
|
// statement, we define useless code as code with no side effects, because
|
|
// the main effect, the value left on the stack after the code executes,
|
|
// will be discarded by a pop bytecode.
|
|
MOZ_MUST_USE bool checkSideEffects(ParseNode* pn, bool* answer);
|
|
|
|
#ifdef DEBUG
|
|
MOZ_MUST_USE bool checkStrictOrSloppy(JSOp op);
|
|
#endif
|
|
|
|
// Append a new source note of the given type (and therefore size) to the
|
|
// notes dynamic array, updating noteCount. Return the new note's index
|
|
// within the array pointed at by current->notes as outparam.
|
|
MOZ_MUST_USE bool newSrcNote(SrcNoteType type, unsigned* indexp = nullptr);
|
|
MOZ_MUST_USE bool newSrcNote2(SrcNoteType type, ptrdiff_t offset, unsigned* indexp = nullptr);
|
|
MOZ_MUST_USE bool newSrcNote3(SrcNoteType type, ptrdiff_t offset1, ptrdiff_t offset2,
|
|
unsigned* indexp = nullptr);
|
|
|
|
void copySrcNotes(jssrcnote* destination, uint32_t nsrcnotes);
|
|
MOZ_MUST_USE bool setSrcNoteOffset(unsigned index, unsigned which, ptrdiff_t offset);
|
|
|
|
// NB: this function can add at most one extra extended delta note.
|
|
MOZ_MUST_USE bool addToSrcNoteDelta(jssrcnote* sn, ptrdiff_t delta);
|
|
|
|
// Finish taking source notes in cx's notePool. If successful, the final
|
|
// source note count is stored in the out outparam.
|
|
MOZ_MUST_USE bool finishTakingSrcNotes(uint32_t* out);
|
|
|
|
// Control whether emitTree emits a line number note.
|
|
enum EmitLineNumberNote {
|
|
EMIT_LINENOTE,
|
|
SUPPRESS_LINENOTE
|
|
};
|
|
|
|
// Emit code for the tree rooted at pn.
|
|
MOZ_MUST_USE bool emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote = EMIT_LINENOTE);
|
|
|
|
// Emit code for the tree rooted at pn with its own TDZ cache.
|
|
MOZ_MUST_USE bool emitTreeInBranch(ParseNode* pn);
|
|
|
|
// Emit global, eval, or module code for tree rooted at body. Always
|
|
// encompasses the entire source.
|
|
MOZ_MUST_USE bool emitScript(ParseNode* body);
|
|
|
|
// Emit function code for the tree rooted at body.
|
|
MOZ_MUST_USE bool emitFunctionScript(ParseNode* body);
|
|
|
|
// If op is JOF_TYPESET (see the type barriers comment in TypeInference.h),
|
|
// reserve a type set to store its result.
|
|
void checkTypeSet(JSOp op);
|
|
|
|
void updateDepth(ptrdiff_t target);
|
|
MOZ_MUST_USE bool updateLineNumberNotes(uint32_t offset);
|
|
MOZ_MUST_USE bool updateSourceCoordNotes(uint32_t offset);
|
|
|
|
JSOp strictifySetNameOp(JSOp op);
|
|
|
|
MOZ_MUST_USE bool emitCheck(ptrdiff_t delta, ptrdiff_t* offset);
|
|
|
|
// Emit one bytecode.
|
|
MOZ_MUST_USE bool emit1(JSOp op);
|
|
|
|
// Emit two bytecodes, an opcode (op) with a byte of immediate operand
|
|
// (op1).
|
|
MOZ_MUST_USE bool emit2(JSOp op, uint8_t op1);
|
|
|
|
// Emit three bytecodes, an opcode with two bytes of immediate operands.
|
|
MOZ_MUST_USE bool emit3(JSOp op, jsbytecode op1, jsbytecode op2);
|
|
|
|
// Helper to emit JSOP_DUPAT. The argument is the value's depth on the
|
|
// JS stack, as measured from the top.
|
|
MOZ_MUST_USE bool emitDupAt(unsigned slotFromTop);
|
|
|
|
// Helper to emit JSOP_CHECKISOBJ.
|
|
MOZ_MUST_USE bool emitCheckIsObj(CheckIsObjectKind kind);
|
|
|
|
// Helper to emit JSOP_CHECKISCALLABLE.
|
|
MOZ_MUST_USE bool emitCheckIsCallable(CheckIsCallableKind kind);
|
|
|
|
// Emit a bytecode followed by an uint16 immediate operand stored in
|
|
// big-endian order.
|
|
MOZ_MUST_USE bool emitUint16Operand(JSOp op, uint32_t operand);
|
|
|
|
// Emit a bytecode followed by an uint32 immediate operand.
|
|
MOZ_MUST_USE bool emitUint32Operand(JSOp op, uint32_t operand);
|
|
|
|
// Emit (1 + extra) bytecodes, for N bytes of op and its immediate operand.
|
|
MOZ_MUST_USE bool emitN(JSOp op, size_t extra, ptrdiff_t* offset = nullptr);
|
|
|
|
MOZ_MUST_USE bool emitNumberOp(double dval);
|
|
|
|
MOZ_MUST_USE bool emitThisLiteral(ParseNode* pn);
|
|
MOZ_MUST_USE bool emitGetFunctionThis(ParseNode* pn);
|
|
MOZ_MUST_USE bool emitGetThisForSuperBase(ParseNode* pn);
|
|
MOZ_MUST_USE bool emitSetThis(ParseNode* pn);
|
|
MOZ_MUST_USE bool emitCheckDerivedClassConstructorReturn();
|
|
|
|
// Handle jump opcodes and jump targets.
|
|
MOZ_MUST_USE bool emitJumpTarget(JumpTarget* target);
|
|
MOZ_MUST_USE bool emitJumpNoFallthrough(JSOp op, JumpList* jump);
|
|
MOZ_MUST_USE bool emitJump(JSOp op, JumpList* jump);
|
|
MOZ_MUST_USE bool emitBackwardJump(JSOp op, JumpTarget target, JumpList* jump,
|
|
JumpTarget* fallthrough);
|
|
void patchJumpsToTarget(JumpList jump, JumpTarget target);
|
|
MOZ_MUST_USE bool emitJumpTargetAndPatch(JumpList jump);
|
|
|
|
MOZ_MUST_USE bool emitCall(JSOp op, uint16_t argc, ParseNode* pn = nullptr);
|
|
MOZ_MUST_USE bool emitCallIncDec(ParseNode* incDec);
|
|
|
|
MOZ_MUST_USE bool emitLoopHead(ParseNode* nextpn, JumpTarget* top);
|
|
MOZ_MUST_USE bool emitLoopEntry(ParseNode* nextpn, JumpList entryJump);
|
|
|
|
MOZ_MUST_USE bool emitGoto(NestableControl* target, JumpList* jumplist,
|
|
SrcNoteType noteType = SRC_NULL);
|
|
|
|
MOZ_MUST_USE bool emitIndex32(JSOp op, uint32_t index);
|
|
MOZ_MUST_USE bool emitIndexOp(JSOp op, uint32_t index);
|
|
|
|
MOZ_MUST_USE bool emitAtomOp(JSAtom* atom, JSOp op);
|
|
MOZ_MUST_USE bool emitAtomOp(ParseNode* pn, JSOp op);
|
|
|
|
MOZ_MUST_USE bool emitArrayLiteral(ParseNode* pn);
|
|
MOZ_MUST_USE bool emitArray(ParseNode* pn, uint32_t count, JSOp op);
|
|
MOZ_MUST_USE bool emitArrayComp(ParseNode* pn);
|
|
|
|
MOZ_MUST_USE bool emitInternedScopeOp(uint32_t index, JSOp op);
|
|
MOZ_MUST_USE bool emitInternedObjectOp(uint32_t index, JSOp op);
|
|
MOZ_MUST_USE bool emitObjectOp(ObjectBox* objbox, JSOp op);
|
|
MOZ_MUST_USE bool emitObjectPairOp(ObjectBox* objbox1, ObjectBox* objbox2, JSOp op);
|
|
MOZ_MUST_USE bool emitRegExp(uint32_t index);
|
|
|
|
MOZ_NEVER_INLINE MOZ_MUST_USE bool emitFunction(ParseNode* pn, bool needsProto = false);
|
|
MOZ_NEVER_INLINE MOZ_MUST_USE bool emitObject(ParseNode* pn);
|
|
|
|
MOZ_MUST_USE bool emitHoistedFunctionsInList(ParseNode* pn);
|
|
|
|
MOZ_MUST_USE bool emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp,
|
|
PropListType type);
|
|
|
|
// To catch accidental misuse, emitUint16Operand/emit3 assert that they are
|
|
// not used to unconditionally emit JSOP_GETLOCAL. Variable access should
|
|
// instead be emitted using EmitVarOp. In special cases, when the caller
|
|
// definitely knows that a given local slot is unaliased, this function may be
|
|
// used as a non-asserting version of emitUint16Operand.
|
|
MOZ_MUST_USE bool emitLocalOp(JSOp op, uint32_t slot);
|
|
|
|
MOZ_MUST_USE bool emitArgOp(JSOp op, uint16_t slot);
|
|
MOZ_MUST_USE bool emitEnvCoordOp(JSOp op, EnvironmentCoordinate ec);
|
|
|
|
MOZ_MUST_USE bool emitGetNameAtLocation(JSAtom* name, const NameLocation& loc,
|
|
bool callContext = false);
|
|
MOZ_MUST_USE bool emitGetName(JSAtom* name, bool callContext = false) {
|
|
return emitGetNameAtLocation(name, lookupName(name), callContext);
|
|
}
|
|
MOZ_MUST_USE bool emitGetName(ParseNode* pn, bool callContext = false);
|
|
|
|
template <typename RHSEmitter>
|
|
MOZ_MUST_USE bool emitSetOrInitializeNameAtLocation(HandleAtom name, const NameLocation& loc,
|
|
RHSEmitter emitRhs, bool initialize);
|
|
template <typename RHSEmitter>
|
|
MOZ_MUST_USE bool emitSetOrInitializeName(HandleAtom name, RHSEmitter emitRhs,
|
|
bool initialize)
|
|
{
|
|
return emitSetOrInitializeNameAtLocation(name, lookupName(name), emitRhs, initialize);
|
|
}
|
|
template <typename RHSEmitter>
|
|
MOZ_MUST_USE bool emitSetName(ParseNode* pn, RHSEmitter emitRhs) {
|
|
RootedAtom name(cx, pn->name());
|
|
return emitSetName(name, emitRhs);
|
|
}
|
|
template <typename RHSEmitter>
|
|
MOZ_MUST_USE bool emitSetName(HandleAtom name, RHSEmitter emitRhs) {
|
|
return emitSetOrInitializeName(name, emitRhs, false);
|
|
}
|
|
template <typename RHSEmitter>
|
|
MOZ_MUST_USE bool emitInitializeName(ParseNode* pn, RHSEmitter emitRhs) {
|
|
RootedAtom name(cx, pn->name());
|
|
return emitInitializeName(name, emitRhs);
|
|
}
|
|
template <typename RHSEmitter>
|
|
MOZ_MUST_USE bool emitInitializeName(HandleAtom name, RHSEmitter emitRhs) {
|
|
return emitSetOrInitializeName(name, emitRhs, true);
|
|
}
|
|
|
|
MOZ_MUST_USE bool emitTDZCheckIfNeeded(JSAtom* name, const NameLocation& loc);
|
|
|
|
MOZ_MUST_USE bool emitNameIncDec(ParseNode* pn);
|
|
|
|
MOZ_MUST_USE bool emitDeclarationList(ParseNode* decls);
|
|
MOZ_MUST_USE bool emitSingleDeclaration(ParseNode* decls, ParseNode* decl,
|
|
ParseNode* initializer);
|
|
|
|
MOZ_MUST_USE bool emitNewInit(JSProtoKey key);
|
|
MOZ_MUST_USE bool emitSingletonInitialiser(ParseNode* pn);
|
|
|
|
MOZ_MUST_USE bool emitPrepareIteratorResult();
|
|
MOZ_MUST_USE bool emitFinishIteratorResult(bool done);
|
|
MOZ_MUST_USE bool iteratorResultShape(unsigned* shape);
|
|
|
|
MOZ_MUST_USE bool emitYield(ParseNode* pn);
|
|
MOZ_MUST_USE bool emitYieldOp(JSOp op);
|
|
MOZ_MUST_USE bool emitYieldStar(ParseNode* iter, ParseNode* gen);
|
|
|
|
MOZ_MUST_USE bool emitPropLHS(ParseNode* pn);
|
|
MOZ_MUST_USE bool emitPropOp(ParseNode* pn, JSOp op);
|
|
MOZ_MUST_USE bool emitPropIncDec(ParseNode* pn);
|
|
|
|
MOZ_MUST_USE bool emitAsyncWrapperLambda(unsigned index, bool isArrow);
|
|
MOZ_MUST_USE bool emitAsyncWrapper(unsigned index, bool needsHomeObject, bool isArrow);
|
|
|
|
MOZ_MUST_USE bool emitComputedPropertyName(ParseNode* computedPropName);
|
|
|
|
// Emit bytecode to put operands for a JSOP_GETELEM/CALLELEM/SETELEM/DELELEM
|
|
// opcode onto the stack in the right order. In the case of SETELEM, the
|
|
// value to be assigned must already be pushed.
|
|
enum class EmitElemOption { Get, Set, Call, IncDec, CompoundAssign, Ref };
|
|
MOZ_MUST_USE bool emitElemOperands(ParseNode* pn, EmitElemOption opts);
|
|
|
|
MOZ_MUST_USE bool emitElemOpBase(JSOp op);
|
|
MOZ_MUST_USE bool emitElemOp(ParseNode* pn, JSOp op);
|
|
MOZ_MUST_USE bool emitElemIncDec(ParseNode* pn);
|
|
|
|
MOZ_MUST_USE bool emitCatch(ParseNode* pn);
|
|
MOZ_MUST_USE bool emitIf(ParseNode* pn);
|
|
MOZ_MUST_USE bool emitWith(ParseNode* pn);
|
|
|
|
MOZ_NEVER_INLINE MOZ_MUST_USE bool emitLabeledStatement(const LabeledStatement* pn);
|
|
MOZ_NEVER_INLINE MOZ_MUST_USE bool emitLexicalScope(ParseNode* pn);
|
|
MOZ_MUST_USE bool emitLexicalScopeBody(ParseNode* body,
|
|
EmitLineNumberNote emitLineNote = EMIT_LINENOTE);
|
|
MOZ_NEVER_INLINE MOZ_MUST_USE bool emitSwitch(ParseNode* pn);
|
|
MOZ_NEVER_INLINE MOZ_MUST_USE bool emitTry(ParseNode* pn);
|
|
|
|
enum DestructuringFlavor {
|
|
// Destructuring into a declaration.
|
|
DestructuringDeclaration,
|
|
|
|
// Destructuring into a formal parameter, when the formal parameters
|
|
// contain an expression that might be evaluated, and thus require
|
|
// this destructuring to assign not into the innermost scope that
|
|
// contains the function body's vars, but into its enclosing scope for
|
|
// parameter expressions.
|
|
DestructuringFormalParameterInVarScope,
|
|
|
|
// Destructuring as part of an AssignmentExpression.
|
|
DestructuringAssignment
|
|
};
|
|
|
|
// emitDestructuringLHSRef emits the lhs expression's reference.
|
|
// If the lhs expression is object property |OBJ.prop|, it emits |OBJ|.
|
|
// If it's object element |OBJ[ELEM]|, it emits |OBJ| and |ELEM|.
|
|
// If there's nothing to evaluate for the reference, it emits nothing.
|
|
// |emitted| parameter receives the number of values pushed onto the stack.
|
|
MOZ_MUST_USE bool emitDestructuringLHSRef(ParseNode* target, size_t* emitted);
|
|
|
|
// emitSetOrInitializeDestructuring assumes the lhs expression's reference
|
|
// and the to-be-destructured value has been pushed on the stack. It emits
|
|
// code to destructure a single lhs expression (either a name or a compound
|
|
// []/{} expression).
|
|
MOZ_MUST_USE bool emitSetOrInitializeDestructuring(ParseNode* target, DestructuringFlavor flav);
|
|
|
|
// emitDestructuringOps assumes the to-be-destructured value has been
|
|
// pushed on the stack and emits code to destructure each part of a [] or
|
|
// {} lhs expression.
|
|
MOZ_MUST_USE bool emitDestructuringOps(ParseNode* pattern, DestructuringFlavor flav);
|
|
MOZ_MUST_USE bool emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlavor flav);
|
|
MOZ_MUST_USE bool emitDestructuringOpsObject(ParseNode* pattern, DestructuringFlavor flav);
|
|
|
|
typedef bool
|
|
(*DestructuringDeclEmitter)(BytecodeEmitter* bce, ParseNode* pn);
|
|
|
|
template <typename NameEmitter>
|
|
MOZ_MUST_USE bool emitDestructuringDeclsWithEmitter(ParseNode* pattern, NameEmitter emitName);
|
|
|
|
// Throw a TypeError if the value atop the stack isn't convertible to an
|
|
// object, with no overall effect on the stack.
|
|
MOZ_MUST_USE bool emitRequireObjectCoercible();
|
|
|
|
// emitIterator expects the iterable to already be on the stack.
|
|
// It will replace that stack value with the corresponding iterator
|
|
MOZ_MUST_USE bool emitIterator();
|
|
|
|
// Pops iterator from the top of the stack. Pushes the result of |.next()|
|
|
// onto the stack.
|
|
MOZ_MUST_USE bool emitIteratorNext(ParseNode* pn, bool allowSelfHosted = false);
|
|
MOZ_MUST_USE bool emitIteratorClose(CompletionKind completionKind = CompletionKind::Normal,
|
|
bool allowSelfHosted = false);
|
|
|
|
template <typename InnerEmitter>
|
|
MOZ_MUST_USE bool wrapWithDestructuringIteratorCloseTryNote(int32_t iterDepth,
|
|
InnerEmitter emitter);
|
|
|
|
// Check if the value on top of the stack is "undefined". If so, replace
|
|
// that value on the stack with the value defined by |defaultExpr|.
|
|
// |pattern| is a lhs node of the default expression. If it's an
|
|
// identifier and |defaultExpr| is an anonymous function, |SetFunctionName|
|
|
// is called at compile time.
|
|
MOZ_MUST_USE bool emitDefault(ParseNode* defaultExpr, ParseNode* pattern);
|
|
|
|
MOZ_MUST_USE bool setOrEmitSetFunName(ParseNode* maybeFun, HandleAtom name,
|
|
FunctionPrefixKind prefixKind);
|
|
|
|
MOZ_MUST_USE bool emitInitializer(ParseNode* initializer, ParseNode* pattern);
|
|
MOZ_MUST_USE bool emitInitializerInBranch(ParseNode* initializer, ParseNode* pattern);
|
|
|
|
MOZ_MUST_USE bool emitCallSiteObject(ParseNode* pn);
|
|
MOZ_MUST_USE bool emitTemplateString(ParseNode* pn);
|
|
MOZ_MUST_USE bool emitAssignment(ParseNode* lhs, JSOp op, ParseNode* rhs);
|
|
|
|
MOZ_MUST_USE bool emitReturn(ParseNode* pn);
|
|
MOZ_MUST_USE bool emitStatement(ParseNode* pn);
|
|
MOZ_MUST_USE bool emitStatementList(ParseNode* pn);
|
|
|
|
MOZ_MUST_USE bool emitDeleteName(ParseNode* pn);
|
|
MOZ_MUST_USE bool emitDeleteProperty(ParseNode* pn);
|
|
MOZ_MUST_USE bool emitDeleteElement(ParseNode* pn);
|
|
MOZ_MUST_USE bool emitDeleteExpression(ParseNode* pn);
|
|
|
|
// |op| must be JSOP_TYPEOF or JSOP_TYPEOFEXPR.
|
|
MOZ_MUST_USE bool emitTypeof(ParseNode* node, JSOp op);
|
|
|
|
MOZ_MUST_USE bool emitUnary(ParseNode* pn);
|
|
MOZ_MUST_USE bool emitRightAssociative(ParseNode* pn);
|
|
MOZ_MUST_USE bool emitLeftAssociative(ParseNode* pn);
|
|
MOZ_MUST_USE bool emitLogical(ParseNode* pn);
|
|
MOZ_MUST_USE bool emitSequenceExpr(ParseNode* pn);
|
|
|
|
MOZ_NEVER_INLINE MOZ_MUST_USE bool emitIncOrDec(ParseNode* pn);
|
|
|
|
MOZ_MUST_USE bool emitConditionalExpression(ConditionalExpression& conditional);
|
|
|
|
MOZ_MUST_USE bool isRestParameter(ParseNode* pn, bool* result);
|
|
MOZ_MUST_USE bool emitOptimizeSpread(ParseNode* arg0, JumpList* jmp, bool* emitted);
|
|
|
|
MOZ_MUST_USE bool emitCallOrNew(ParseNode* pn);
|
|
MOZ_MUST_USE bool emitSelfHostedCallFunction(ParseNode* pn);
|
|
MOZ_MUST_USE bool emitSelfHostedResumeGenerator(ParseNode* pn);
|
|
MOZ_MUST_USE bool emitSelfHostedForceInterpreter(ParseNode* pn);
|
|
MOZ_MUST_USE bool emitSelfHostedAllowContentIter(ParseNode* pn);
|
|
|
|
MOZ_MUST_USE bool emitComprehensionFor(ParseNode* compFor);
|
|
MOZ_MUST_USE bool emitComprehensionForIn(ParseNode* pn);
|
|
MOZ_MUST_USE bool emitComprehensionForInOrOfVariables(ParseNode* pn, bool* lexicalScope);
|
|
MOZ_MUST_USE bool emitComprehensionForOf(ParseNode* pn);
|
|
|
|
MOZ_MUST_USE bool emitDo(ParseNode* pn);
|
|
MOZ_MUST_USE bool emitWhile(ParseNode* pn);
|
|
|
|
MOZ_MUST_USE bool emitFor(ParseNode* pn, EmitterScope* headLexicalEmitterScope = nullptr);
|
|
MOZ_MUST_USE bool emitCStyleFor(ParseNode* pn, EmitterScope* headLexicalEmitterScope);
|
|
MOZ_MUST_USE bool emitForIn(ParseNode* pn, EmitterScope* headLexicalEmitterScope);
|
|
MOZ_MUST_USE bool emitForOf(ParseNode* pn, EmitterScope* headLexicalEmitterScope);
|
|
|
|
MOZ_MUST_USE bool emitInitializeForInOrOfTarget(ParseNode* forHead);
|
|
|
|
MOZ_MUST_USE bool emitBreak(PropertyName* label);
|
|
MOZ_MUST_USE bool emitContinue(PropertyName* label);
|
|
|
|
MOZ_MUST_USE bool emitFunctionFormalParametersAndBody(ParseNode* pn);
|
|
MOZ_MUST_USE bool emitFunctionFormalParameters(ParseNode* pn);
|
|
MOZ_MUST_USE bool emitInitializeFunctionSpecialNames();
|
|
MOZ_MUST_USE bool emitFunctionBody(ParseNode* pn);
|
|
MOZ_MUST_USE bool emitLexicalInitialization(ParseNode* pn);
|
|
|
|
// Emit bytecode for the spread operator.
|
|
//
|
|
// emitSpread expects the current index (I) of the array, the array itself
|
|
// and the iterator to be on the stack in that order (iterator on the bottom).
|
|
// It will pop the iterator and I, then iterate over the iterator by calling
|
|
// |.next()| and put the results into the I-th element of array with
|
|
// incrementing I, then push the result I (it will be original I +
|
|
// iteration count). The stack after iteration will look like |ARRAY INDEX|.
|
|
MOZ_MUST_USE bool emitSpread(bool allowSelfHosted = false);
|
|
|
|
MOZ_MUST_USE bool emitClass(ParseNode* pn);
|
|
MOZ_MUST_USE bool emitSuperPropLHS(ParseNode* superBase, bool isCall = false);
|
|
MOZ_MUST_USE bool emitSuperPropOp(ParseNode* pn, JSOp op, bool isCall = false);
|
|
MOZ_MUST_USE bool emitSuperElemOperands(ParseNode* pn,
|
|
EmitElemOption opts = EmitElemOption::Get);
|
|
MOZ_MUST_USE bool emitSuperElemOp(ParseNode* pn, JSOp op, bool isCall = false);
|
|
};
|
|
|
|
} /* namespace frontend */
|
|
} /* namespace js */
|
|
|
|
#endif /* frontend_BytecodeEmitter_h */
|