Issue #2155 - Add PropOpEmitter, ElemOpEmitter, NameOpEmitter, CallOrNewEmitter

Attn: this plain patch port still contains issues caused by differences in the
ParseNode tree that will be fixed in the following commits.

Based-on: m-c 1466000/{7-10}
This commit is contained in:
Martok 2023-03-12 22:46:53 +01:00 committed by roytam1
commit 89e6820006
13 changed files with 3240 additions and 1092 deletions

File diff suppressed because it is too large Load diff

View file

@ -18,6 +18,7 @@
#include "frontend/Parser.h"
#include "frontend/SharedContext.h"
#include "frontend/SourceNotes.h"
#include "frontend/ValueUsage.h"
#include "vm/Interpreter.h"
class OptionalEmitter;
@ -119,11 +120,9 @@ static size_t MaxSrcNotesLength = INT32_MAX;
typedef Vector<jsbytecode, 256> BytecodeVector;
typedef Vector<jssrcnote, 64> SrcNotesVector;
enum class ValueUsage {
WantValue,
IgnoreValue
};
class CallOrNewEmitter;
class ElemOpEmitter;
class PropOpEmitter;
class TDZCheckCache;
struct MOZ_STACK_CLASS BytecodeEmitter
@ -447,6 +446,9 @@ struct MOZ_STACK_CLASS BytecodeEmitter
// JS stack, as measured from the top.
MOZ_MUST_USE bool emitDupAt(unsigned slotFromTop);
// Helper to emit JSOP_POP or JSOP_POPN.
MOZ_MUST_USE bool emitPopN(unsigned n);
// Helper to emit JSOP_CHECKISOBJ.
MOZ_MUST_USE bool emitCheckIsObj(CheckIsObjectKind kind);
@ -481,6 +483,8 @@ struct MOZ_STACK_CLASS BytecodeEmitter
void patchJumpsToTarget(JumpList jump, JumpTarget target);
MOZ_MUST_USE bool emitJumpTargetAndPatch(JumpList jump);
MOZ_MUST_USE bool emitCall(JSOp op, uint16_t argc,
const mozilla::Maybe<uint32_t>& sourceCoordOffset);
MOZ_MUST_USE bool emitCall(JSOp op, uint16_t argc, ParseNode* pn = nullptr);
MOZ_MUST_USE bool emitCallIncDec(ParseNode* incDec);
@ -494,6 +498,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter
MOZ_MUST_USE bool emitIndexOp(JSOp op, uint32_t index);
MOZ_MUST_USE bool emitAtomOp(JSAtom* atom, JSOp op);
MOZ_MUST_USE bool emitAtomOp(uint32_t atomIndex, JSOp op);
MOZ_MUST_USE bool emitArrayLiteral(ParseNode* pn);
MOZ_MUST_USE bool emitArray(ParseNode* pn, uint32_t count, JSOp op);
@ -531,37 +536,9 @@ struct MOZ_STACK_CLASS BytecodeEmitter
}
MOZ_MUST_USE bool emitGetName(ParseNode* pn);
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 emitNameIncDec(ParseNode* incDec);
MOZ_MUST_USE bool emitDeclarationList(ParseNode* decls);
MOZ_MUST_USE bool emitSingleDeclaration(ParseNode* decls, ParseNode* decl,
@ -590,7 +567,6 @@ struct MOZ_STACK_CLASS BytecodeEmitter
MOZ_MUST_USE bool emitAwaitInInnermostScope(ParseNode* pn);
MOZ_MUST_USE bool emitAwaitInScope(EmitterScope& currentScope);
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);
@ -605,6 +581,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter
enum class EmitElemOption { Get, Set, Call, IncDec, CompoundAssign, Ref };
MOZ_MUST_USE bool emitElemOperands(ParseNode* pn, EmitElemOption opts);
MOZ_MUST_USE bool emitElemObjAndKey(PropertyByValue* elem, bool isSuper, ElemOpEmitter& eoe);
MOZ_MUST_USE bool emitElemOpBase(JSOp op);
MOZ_MUST_USE bool emitElemOp(ParseNode* pn, JSOp op);
MOZ_MUST_USE bool emitElemIncDec(ParseNode* pn);
@ -715,7 +692,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter
MOZ_MUST_USE bool emitDefault(ParseNode* defaultExpr, ParseNode* pattern);
MOZ_MUST_USE bool setOrEmitSetFunName(ParseNode* maybeFun, HandleAtom name,
FunctionPrefixKind prefixKind);
FunctionPrefixKind prefixKind = FunctionPrefixKind::None);
MOZ_MUST_USE bool emitInitializer(ParseNode* initializer, ParseNode* pattern);
MOZ_MUST_USE bool emitInitializerInBranch(ParseNode* initializer, ParseNode* pattern);
@ -738,19 +715,17 @@ struct MOZ_STACK_CLASS BytecodeEmitter
ValueUsage valueUsage);
MOZ_MUST_USE bool emitCalleeAndThisForOptionalChain(ParseNode* optionalChain,
ParseNode* callNode,
bool isCall);
CallOrNewEmitter& cone);
MOZ_MUST_USE bool emitDeleteOptionalChain(ParseNode* deleteNode);
// Optional methods which emit a shortCircuit jump. They need to be called by
// a method which emits an Optional Jump Target, see below.
MOZ_MUST_USE bool emitOptionalDotExpression(PropertyAccessBase* prop,
OptionalEmitter& oe,
ParseNode* calleeNode,
bool isCall);
PropOpEmitter& poe, bool isSuper,
OptionalEmitter& oe);
MOZ_MUST_USE bool emitOptionalElemExpression(PropertyByValueBase* elem,
OptionalEmitter& oe,
ParseNode* calleeNode,
bool isCall);
ElemOpEmitter& eoe, bool isSuper,
OptionalEmitter& oe);
MOZ_MUST_USE bool emitOptionalCall(ParseNode* callNode,
OptionalEmitter& oe,
ValueUsage valueUsage);
@ -777,14 +752,18 @@ struct MOZ_STACK_CLASS BytecodeEmitter
MOZ_MUST_USE bool isRestParameter(ParseNode* pn);
MOZ_MUST_USE bool emitOptimizeSpread(ParseNode* arg0, JumpList* jmp, bool* emitted);
MOZ_MUST_USE ParseNode* getCoordNode(ParseNode* callNode, ParseNode* calleeNode,
ParseNode* argsList);
MOZ_MUST_USE bool emitArguments(ListNode* argsList, bool isCall, bool isSpread,
CallOrNewEmitter& cone);
MOZ_MUST_USE bool emitCallOrNew(ParseNode* pn,
ValueUsage valueUsage = ValueUsage::WantValue);
MOZ_MUST_USE bool emitCalleeAndThis(ParseNode* callNode,
ParseNode* calleeNode,
bool isCall);
CallOrNewEmitter& cone);
MOZ_MUST_USE bool emitOptionalCalleeAndThis(ParseNode* callNode,
ParseNode* calleeNode,
bool isCall,
CallOrNewEmitter& cone,
OptionalEmitter& oe);
MOZ_MUST_USE bool emitCallOrNewThis(ParseNode* callNode,
bool isCall);
@ -833,8 +812,6 @@ struct MOZ_STACK_CLASS BytecodeEmitter
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);

View file

@ -0,0 +1,319 @@
/* -*- 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/. */
#include "frontend/CallOrNewEmitter.h"
#include "frontend/BytecodeEmitter.h"
#include "frontend/NameOpEmitter.h"
#include "frontend/SharedContext.h"
#include "vm/Opcodes.h"
#include "vm/String.h"
using namespace js;
using namespace js::frontend;
using mozilla::Maybe;
AutoEmittingRunOnceLambda::AutoEmittingRunOnceLambda(BytecodeEmitter* bce)
: bce_(bce)
{
MOZ_ASSERT(!bce_->emittingRunOnceLambda);
bce_->emittingRunOnceLambda = true;
}
AutoEmittingRunOnceLambda::~AutoEmittingRunOnceLambda()
{
bce_->emittingRunOnceLambda = false;
}
CallOrNewEmitter::CallOrNewEmitter(BytecodeEmitter* bce, JSOp op,
ArgumentsKind argumentsKind,
ValueUsage valueUsage)
: bce_(bce),
op_(op),
argumentsKind_(argumentsKind)
{
if (op_ == JSOP_CALL && valueUsage == ValueUsage::IgnoreValue) {
op_ = JSOP_CALL_IGNORES_RV;
}
MOZ_ASSERT(isCall() || isNew() || isSuperCall());
}
bool
CallOrNewEmitter::emitNameCallee(JSAtom* name)
{
MOZ_ASSERT(state_ == State::Start);
NameOpEmitter noe(bce_, name,
isCall()
? NameOpEmitter::Kind::Call
: NameOpEmitter::Kind::Get);
if (!noe.emitGet()) { // CALLEE THIS
return false;
}
state_ = State::NameCallee;
return true;
}
MOZ_MUST_USE PropOpEmitter&
CallOrNewEmitter::prepareForPropCallee(bool isSuperProp)
{
MOZ_ASSERT(state_ == State::Start);
poe_.emplace(bce_,
isCall()
? PropOpEmitter::Kind::Call
: PropOpEmitter::Kind::Get,
isSuperProp
? PropOpEmitter::ObjKind::Super
: PropOpEmitter::ObjKind::Other);
state_ = State::PropCallee;
return *poe_;
}
MOZ_MUST_USE ElemOpEmitter&
CallOrNewEmitter::prepareForElemCallee(bool isSuperElem)
{
MOZ_ASSERT(state_ == State::Start);
eoe_.emplace(bce_,
isCall()
? ElemOpEmitter::Kind::Call
: ElemOpEmitter::Kind::Get,
isSuperElem
? ElemOpEmitter::ObjKind::Super
: ElemOpEmitter::ObjKind::Other);
state_ = State::ElemCallee;
return *eoe_;
}
bool
CallOrNewEmitter::prepareForFunctionCallee()
{
MOZ_ASSERT(state_ == State::Start);
// Top level lambdas which are immediately invoked should be treated as
// only running once. Every time they execute we will create new types and
// scripts for their contents, to increase the quality of type information
// within them and enable more backend optimizations. Note that this does
// not depend on the lambda being invoked at most once (it may be named or
// be accessed via foo.caller indirection), as multiple executions will
// just cause the inner scripts to be repeatedly cloned.
MOZ_ASSERT(!bce_->emittingRunOnceLambda);
if (bce_->checkRunOnceContext()) {
autoEmittingRunOnceLambda_.emplace(bce_);
}
state_ = State::FunctionCallee;
return true;
}
bool
CallOrNewEmitter::emitSuperCallee()
{
MOZ_ASSERT(state_ == State::Start);
if (!bce_->emit1(JSOP_SUPERFUN)) { // CALLEE
return false;
}
if (!bce_->emit1(JSOP_IS_CONSTRUCTING)) { // CALLEE THIS
return false;
}
state_ = State::SuperCallee;
return true;
}
bool
CallOrNewEmitter::prepareForOtherCallee()
{
MOZ_ASSERT(state_ == State::Start);
state_ = State::OtherCallee;
return true;
}
bool
CallOrNewEmitter::emitThis()
{
MOZ_ASSERT(state_ == State::NameCallee ||
state_ == State::PropCallee ||
state_ == State::ElemCallee ||
state_ == State::FunctionCallee ||
state_ == State::SuperCallee ||
state_ == State::OtherCallee);
bool needsThis = false;
switch (state_) {
case State::NameCallee:
if (!isCall()) {
needsThis = true;
}
break;
case State::PropCallee:
poe_.reset();
if (!isCall()) {
needsThis = true;
}
break;
case State::ElemCallee:
eoe_.reset();
if (!isCall()) {
needsThis = true;
}
break;
case State::FunctionCallee:
autoEmittingRunOnceLambda_.reset();
needsThis = true;
break;
case State::SuperCallee:
break;
case State::OtherCallee:
needsThis = true;
break;
default:;
}
if (needsThis) {
if (isNew() || isSuperCall()) {
if (!bce_->emit1(JSOP_IS_CONSTRUCTING)) { // CALLEE THIS
return false;
}
} else {
if (!bce_->emit1(JSOP_UNDEFINED)) { // CALLEE THIS
return false;
}
}
}
state_ = State::This;
return true;
}
// Used by BytecodeEmitter::emitPipeline to reuse CallOrNewEmitter instance
// across multiple chained calls.
void
CallOrNewEmitter::reset()
{
MOZ_ASSERT(state_ == State::End);
state_ = State::Start;
}
bool
CallOrNewEmitter::prepareForNonSpreadArguments()
{
MOZ_ASSERT(state_ == State::This);
MOZ_ASSERT(!isSpread());
state_ = State::Arguments;
return true;
}
// See the usage in the comment at the top of the class.
bool
CallOrNewEmitter::wantSpreadOperand()
{
MOZ_ASSERT(state_ == State::This);
MOZ_ASSERT(isSpread());
state_ = State::WantSpreadOperand;
return isSingleSpreadRest();
}
bool
CallOrNewEmitter::emitSpreadArgumentsTest()
{
// Caller should check wantSpreadOperand before this.
MOZ_ASSERT(state_ == State::WantSpreadOperand);
MOZ_ASSERT(isSpread());
if (isSingleSpreadRest()) {
// Emit a preparation code to optimize the spread call with a rest
// parameter:
//
// function f(...args) {
// g(...args);
// }
//
// If the spread operand is a rest parameter and it's optimizable
// array, skip spread operation and pass it directly to spread call
// operation. See the comment in OptimizeSpreadCall in
// Interpreter.cpp for the optimizable conditons.
ifNotOptimizable_.emplace(bce_);
// // CALLEE THIS ARG0
if (!bce_->emit1(JSOP_OPTIMIZE_SPREADCALL)) { // CALLEE THIS ARG0 OPTIMIZED
return false;
}
if (!bce_->emit1(JSOP_NOT)) { // CALLEE THIS ARG0 !OPTIMIZED
return false;
}
if (!ifNotOptimizable_->emitThen()) { // CALLEE THIS ARG0
return false;
}
if (!bce_->emit1(JSOP_POP)) { // CALLEE THIS
return false;
}
}
state_ = State::Arguments;
return true;
}
bool
CallOrNewEmitter::emitEnd(uint32_t argc, const Maybe<uint32_t>& beginPos)
{
MOZ_ASSERT(state_ == State::Arguments);
if (isSingleSpreadRest()) {
if (!ifNotOptimizable_->emitEnd()) { // CALLEE THIS ARR
return false;
}
ifNotOptimizable_.reset();
}
if (isNew() || isSuperCall()) {
if (isSuperCall()) {
if (!bce_->emit1(JSOP_NEWTARGET)) { // CALLEE THIS ARG.. NEW.TARGET
return false;
}
} else {
// Repush the callee as new.target
uint32_t effectiveArgc = isSpread() ? 1 : argc;
if (!bce_->emitDupAt(effectiveArgc + 1)) {
return false; // CALLEE THIS ARR CALLEE
}
}
}
if (!isSpread()) {
if (!bce_->emitCall(op_, argc, beginPos)) { // RVAL
return false;
}
} else {
if (beginPos) {
if (!bce_->updateSourceCoordNotes(*beginPos)) {
return false;
}
}
if (!bce_->emit1(op_)) { // RVAL
return false;
}
}
bce_->checkTypeSet(op_);
if (isEval() && beginPos) {
uint32_t lineNum = bce_->parser->tokenStream.srcCoords.lineNum(*beginPos);
if (!bce_->emitUint32Operand(JSOP_LINENO, lineNum)) {
return false;
}
}
state_ = State::End;
return true;
}

View file

@ -0,0 +1,338 @@
/* -*- 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/. */
#ifndef frontend_CallOrNewEmitter_h
#define frontend_CallOrNewEmitter_h
#include "mozilla/Attributes.h"
#include "mozilla/Maybe.h"
#include <stdint.h>
#include "frontend/ElemOpEmitter.h"
#include "frontend/IfEmitter.h"
#include "frontend/PropOpEmitter.h"
#include "frontend/ValueUsage.h"
#include "js/TypeDecls.h"
#include "vm/Opcodes.h"
#include "jsopcode.h"
namespace js {
namespace frontend {
struct BytecodeEmitter;
class MOZ_RAII AutoEmittingRunOnceLambda
{
BytecodeEmitter* bce_;
public:
explicit AutoEmittingRunOnceLambda(BytecodeEmitter* bce);
~AutoEmittingRunOnceLambda();
};
// Class for emitting bytecode for call or new expression.
//
// Usage: (check for the return value is omitted for simplicity)
//
// `print(arg);`
// CallOrNewEmitter cone(this, JSOP_CALL,
// CallOrNewEmitter::ArgumentsKind::Other,
// ValueUsage::WantValue);
// cone.emitNameCallee();
// emit(print);
// cone.emitThis();
// cone.prepareForNonSpreadArguments();
// emit(arg);
// cone.emitEnd(1, Some(offset_of_callee));
//
// `callee.prop(arg1, arg2);`
// CallOrNewEmitter cone(this, JSOP_CALL,
// CallOrNewEmitter::ArgumentsKind::Other,
// ValueUsage::WantValue);
// PropOpEmitter& poe = cone.prepareForPropCallee(false);
// ... emit `callee.prop` with `poe` here...
// cone.emitThis();
// cone.prepareForNonSpreadArguments();
// emit(arg1);
// emit(arg2);
// cone.emitEnd(2, Some(offset_of_callee));
//
// `callee[key](arg);`
// CallOrNewEmitter cone(this, JSOP_CALL,
// CallOrNewEmitter::ArgumentsKind::Other,
// ValueUsage::WantValue);
// ElemOpEmitter& eoe = cone.prepareForElemCallee(false);
// ... emit `callee[key]` with `eoe` here...
// cone.emitThis();
// cone.prepareForNonSpreadArguments();
// emit(arg);
// cone.emitEnd(1, Some(offset_of_callee));
//
// `(function() { ... })(arg);`
// CallOrNewEmitter cone(this, JSOP_CALL,
// CallOrNewEmitter::ArgumentsKind::Other,
// ValueUsage::WantValue);
// cone.prepareForFunctionCallee();
// emit(function);
// cone.emitThis();
// cone.prepareForNonSpreadArguments();
// emit(arg);
// cone.emitEnd(1, Some(offset_of_callee));
//
// `super(arg);`
// CallOrNewEmitter cone(this, JSOP_CALL,
// CallOrNewEmitter::ArgumentsKind::Other,
// ValueUsage::WantValue);
// cone.emitSuperCallee();
// cone.emitThis();
// cone.prepareForNonSpreadArguments();
// emit(arg);
// cone.emitEnd(1, Some(offset_of_callee));
//
// `(some_other_expression)(arg);`
// CallOrNewEmitter cone(this, JSOP_CALL,
// CallOrNewEmitter::ArgumentsKind::Other,
// ValueUsage::WantValue);
// cone.prepareForOtherCallee();
// emit(some_other_expression);
// cone.emitThis();
// cone.prepareForNonSpreadArguments();
// emit(arg);
// cone.emitEnd(1, Some(offset_of_callee));
//
// `print(...arg);`
// CallOrNewEmitter cone(this, JSOP_SPREADCALL,
// CallOrNewEmitter::ArgumentsKind::Other,
// ValueUsage::WantValue);
// cone.emitNameCallee();
// emit(print);
// cone.emitThis();
// if (cone.wantSpreadOperand())
// emit(arg)
// cone.emitSpreadArgumentsTest();
// emit([...arg]);
// cone.emitEnd(1, Some(offset_of_callee));
//
// `print(...rest);`
// where `rest` is rest parameter
// CallOrNewEmitter cone(this, JSOP_SPREADCALL,
// CallOrNewEmitter::ArgumentsKind::SingleSpreadRest,
// ValueUsage::WantValue);
// cone.emitNameCallee();
// emit(print);
// cone.emitThis();
// if (cone.wantSpreadOperand())
// emit(arg)
// cone.emitSpreadArgumentsTest();
// emit([...arg]);
// cone.emitEnd(1, Some(offset_of_callee));
//
// `new f(arg);`
// CallOrNewEmitter cone(this, JSOP_NEW,
// CallOrNewEmitter::ArgumentsKind::Other,
// ValueUsage::WantValue);
// cone.emitNameCallee();
// emit(f);
// cone.emitThis();
// cone.prepareForNonSpreadArguments();
// emit(arg);
// cone.emitEnd(1, Some(offset_of_callee));
//
class MOZ_STACK_CLASS CallOrNewEmitter
{
public:
enum class ArgumentsKind {
Other,
// Specify this for the following case:
//
// function f(...rest) {
// g(...rest);
// }
//
// This enables optimization to avoid allocating an intermediate array
// for spread operation.
//
// wantSpreadOperand() returns true when this is specified.
SingleSpreadRest
};
private:
BytecodeEmitter* bce_;
// The opcode for the call or new.
JSOp op_;
// Whether the call is a spread call with single rest parameter or not.
// See the comment in emitSpreadArgumentsTest for more details.
ArgumentsKind argumentsKind_;
// The branch for spread call optimization.
mozilla::Maybe<InternalIfEmitter> ifNotOptimizable_;
mozilla::Maybe<AutoEmittingRunOnceLambda> autoEmittingRunOnceLambda_;
mozilla::Maybe<PropOpEmitter> poe_;
mozilla::Maybe<ElemOpEmitter> eoe_;
// The state of this emitter.
//
// +-------+ emitNameCallee +------------+
// | Start |-+------------------------->| NameCallee |------+
// +-------+ | +------------+ |
// | |
// | prepareForPropCallee +------------+ v
// +------------------------->| PropCallee |----->+
// | +------------+ |
// | |
// | prepareForElemCallee +------------+ v
// +------------------------->| ElemCallee |----->+
// | +------------+ |
// | |
// | prepareForFunctionCallee +----------------+ v
// +------------------------->| FunctionCallee |->+
// | +----------------+ |
// | |
// | emitSuperCallee +-------------+ v
// +------------------------->| SuperCallee |---->+
// | +-------------+ |
// | |
// | prepareForOtherCallee +-------------+ v
// +------------------------->| OtherCallee |---->+
// +-------------+ |
// |
// +--------------------------------------------------------+
// |
// | emitThis +------+
// +--------->| This |-+
// +------+ |
// |
// +-------------------+
// |
// | [!isSpread]
// | prepareForNonSpreadArguments +-----------+ emitEnd +-----+
// +------------------------------->+->| Arguments |-------->| End |
// | ^ +-----------+ +-----+
// | |
// | +----------------------------------+
// | |
// | [isSpread] |
// | wantSpreadOperand +-------------------+ emitSpreadArgumentsTest |
// +-------------------->| WantSpreadOperand |-------------------------+
// +-------------------+
enum class State {
// The initial state.
Start,
// After calling emitNameCallee.
NameCallee,
// After calling prepareForPropCallee.
PropCallee,
// After calling prepareForElemCallee.
ElemCallee,
// After calling prepareForFunctionCallee.
FunctionCallee,
// After calling emitSuperCallee.
SuperCallee,
// After calling prepareForOtherCallee.
OtherCallee,
// After calling emitThis.
This,
// After calling wantSpreadOperand.
WantSpreadOperand,
// After calling prepareForNonSpreadArguments.
Arguments,
// After calling emitEnd.
End
};
State state_ = State::Start;
public:
CallOrNewEmitter(BytecodeEmitter* bce, JSOp op,
ArgumentsKind argumentsKind,
ValueUsage valueUsage);
private:
MOZ_MUST_USE bool isCall() const {
return op_ == JSOP_CALL || op_ == JSOP_CALL_IGNORES_RV ||
op_ == JSOP_SPREADCALL ||
isEval() || isFunApply() || isFunCall();
}
MOZ_MUST_USE bool isNew() const {
return op_ == JSOP_NEW || op_ == JSOP_SPREADNEW;
}
MOZ_MUST_USE bool isSuperCall() const {
return op_ == JSOP_SUPERCALL || op_ == JSOP_SPREADSUPERCALL;
}
MOZ_MUST_USE bool isEval() const {
return op_ == JSOP_EVAL || op_ == JSOP_STRICTEVAL ||
op_ == JSOP_SPREADEVAL || op_ == JSOP_STRICTSPREADEVAL;
}
MOZ_MUST_USE bool isFunApply() const {
return op_ == JSOP_FUNAPPLY;
}
MOZ_MUST_USE bool isFunCall() const {
return op_ == JSOP_FUNCALL;
}
MOZ_MUST_USE bool isSpread() const {
return JOF_OPTYPE(op_) == JOF_BYTE;
}
MOZ_MUST_USE bool isSingleSpreadRest() const {
return argumentsKind_ == ArgumentsKind::SingleSpreadRest;
}
public:
MOZ_MUST_USE bool emitNameCallee(JSAtom* name);
MOZ_MUST_USE PropOpEmitter& prepareForPropCallee(bool isSuperProp);
MOZ_MUST_USE ElemOpEmitter& prepareForElemCallee(bool isSuperElem);
MOZ_MUST_USE bool prepareForFunctionCallee();
MOZ_MUST_USE bool emitSuperCallee();
MOZ_MUST_USE bool prepareForOtherCallee();
MOZ_MUST_USE bool emitThis();
// Used by BytecodeEmitter::emitPipeline to reuse CallOrNewEmitter instance
// across multiple chained calls.
void reset();
MOZ_MUST_USE bool prepareForNonSpreadArguments();
// See the usage in the comment at the top of the class.
MOZ_MUST_USE bool wantSpreadOperand();
MOZ_MUST_USE bool emitSpreadArgumentsTest();
// Parameters are the offset in the source code for each character below:
//
// callee(arg);
// ^
// |
// beginPos
//
// Can be Nothing() if not available.
MOZ_MUST_USE bool emitEnd(uint32_t argc, const mozilla::Maybe<uint32_t>& beginPos);
};
} /* namespace frontend */
} /* namespace js */
#endif /* frontend_CallOrNewEmitter_h */

View file

@ -0,0 +1,288 @@
/* -*- 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/. */
#include "frontend/ElemOpEmitter.h"
#include "frontend/BytecodeEmitter.h"
#include "frontend/SharedContext.h"
#include "vm/Opcodes.h"
using namespace js;
using namespace js::frontend;
ElemOpEmitter::ElemOpEmitter(BytecodeEmitter* bce, Kind kind, ObjKind objKind)
: bce_(bce),
kind_(kind),
objKind_(objKind)
{}
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)) { // OBJ
return false;
}
}
if (isCall()) {
if (!bce_->emit1(JSOP_DUP)) { // [Super]
// // THIS THIS
// // [Other]
// // OBJ OBJ
return false;
}
}
#ifdef DEBUG
state_ = State::Key;
#endif
return true;
}
bool
ElemOpEmitter::emitGet()
{
MOZ_ASSERT(state_ == State::Key);
if (isIncDec() || isCompoundAssignment()) {
if (!bce_->emit1(JSOP_TOID)) { // [Super]
// // THIS KEY
// // [Other]
// // OBJ KEY
return false;
}
}
if (isSuper()) {
if (!bce_->emit1(JSOP_SUPERBASE)) { // THIS? THIS KEY SUPERBASE
return false;
}
}
if (isIncDec() || isCompoundAssignment()) {
if (isSuper()) {
// There's no such thing as JSOP_DUP3, so we have to be creative.
// Note that pushing things again is no fewer JSOps.
if (!bce_->emitDupAt(2)) { // THIS KEY SUPERBASE THIS
return false;
}
if (!bce_->emitDupAt(2)) { // THIS KEY SUPERBASE THIS KEY
return false;
}
if (!bce_->emitDupAt(2)) { // THIS KEY SUPERBASE THIS KEY SUPERBASE
return false;
}
} else {
if (!bce_->emit1(JSOP_DUP2)) { // OBJ KEY OBJ KEY
return false;
}
}
}
JSOp op;
if (isSuper()) {
op = JSOP_GETELEM_SUPER;
} else if (isCall()) {
op = JSOP_CALLELEM;
} else {
op = JSOP_GETELEM;
}
if (!bce_->emitElemOpBase(op)) { // [Get]
// // ELEM
// // [Call]
// // THIS ELEM
// // [Inc/Dec/Assignment,
// // Super]
// // THIS KEY SUPERBASE ELEM
// // [Inc/Dec/Assignment,
// // Other]
// // OBJ KEY ELEM
return false;
}
if (isCall()) {
if (!bce_->emit1(JSOP_SWAP)) { // ELEM THIS
return false;
}
}
#ifdef DEBUG
state_ = State::Get;
#endif
return true;
}
bool
ElemOpEmitter::prepareForRhs()
{
MOZ_ASSERT(isSimpleAssignment() || isCompoundAssignment());
MOZ_ASSERT_IF(isSimpleAssignment(), state_ == State::Key);
MOZ_ASSERT_IF(isCompoundAssignment(), state_ == State::Get);
if (isSimpleAssignment()) {
// For CompoundAssignment, SUPERBASE is already emitted by emitGet.
if (isSuper()) {
if (!bce_->emit1(JSOP_SUPERBASE)) { // THIS KEY SUPERBASE
return false;
}
}
}
#ifdef DEBUG
state_ = State::Rhs;
#endif
return true;
}
bool
ElemOpEmitter::skipObjAndKeyAndRhs()
{
MOZ_ASSERT(state_ == State::Start);
MOZ_ASSERT(isSimpleAssignment());
#ifdef DEBUG
state_ = State::Rhs;
#endif
return true;
}
bool
ElemOpEmitter::emitDelete()
{
MOZ_ASSERT(state_ == State::Key);
MOZ_ASSERT(isDelete());
if (isSuper()) {
if (!bce_->emit1(JSOP_TOID)) { // THIS KEY
return false;
}
if (!bce_->emit1(JSOP_SUPERBASE)) { // THIS KEY SUPERBASE
return false;
}
// Unconditionally throw when attempting to delete a super-reference.
if (!bce_->emitUint16Operand(JSOP_THROWMSG, JSMSG_CANT_DELETE_SUPER)) {
return false; // THIS KEY SUPERBASE
}
// 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)) { // THIS
return false;
}
} else {
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() || isCompoundAssignment());
MOZ_ASSERT(state_ == State::Rhs);
JSOp setOp = isSuper()
? bce_->sc->strict() ? JSOP_STRICTSETELEM_SUPER : JSOP_SETELEM_SUPER
: bce_->sc->strict() ? JSOP_STRICTSETELEM : JSOP_SETELEM;
if (!bce_->emitElemOpBase(setOp)) { // ELEM
return false;
}
#ifdef DEBUG
state_ = State::Assignment;
#endif
return true;
}
bool
ElemOpEmitter::emitIncDec()
{
MOZ_ASSERT(state_ == State::Key);
MOZ_ASSERT(isIncDec());
if (!emitGet()) { // ... ELEM
return false;
}
MOZ_ASSERT(state_ == State::Get);
JSOp binOp = isInc() ? JSOP_ADD : JSOP_SUB;
if (!bce_->emit1(JSOP_POS)) { // ... N
return false;
}
if (isPostIncDec()) {
if (!bce_->emit1(JSOP_DUP)) { // ... N? N
return false;
}
}
if (!bce_->emit1(JSOP_ONE)) { // ... N? N 1
return false;
}
if (!bce_->emit1(binOp)) { // ... N? N+1
return false;
}
if (isPostIncDec()) {
if (isSuper()) { // THIS KEY OBJ N N+1
if (!bce_->emit2(JSOP_PICK, 4)) { // KEY SUPERBASE N N+1 THIS
return false;
}
if (!bce_->emit2(JSOP_PICK, 4)) { // SUPERBASE N N+1 THIS KEY
return false;
}
if (!bce_->emit2(JSOP_PICK, 4)) { // N N+1 THIS KEY SUPERBASE
return false;
}
if (!bce_->emit2(JSOP_PICK, 3)) { // N THIS KEY SUPERBASE N+1
return false;
}
} else { // OBJ KEY N N+1
if (!bce_->emit2(JSOP_PICK, 3)) { // KEY N N+1 OBJ
return false;
}
if (!bce_->emit2(JSOP_PICK, 3)) { // N N+1 OBJ KEY
return false;
}
if (!bce_->emit2(JSOP_PICK, 2)) { // N OBJ KEY N+1
return false;
}
}
}
JSOp setOp = isSuper()
? (bce_->sc->strict() ? JSOP_STRICTSETELEM_SUPER : JSOP_SETELEM_SUPER)
: (bce_->sc->strict() ? JSOP_STRICTSETELEM : JSOP_SETELEM);
if (!bce_->emitElemOpBase(setOp)) { // N? N+1
return false;
}
if (isPostIncDec()) {
if (!bce_->emit1(JSOP_POP)) { // N
return false;
}
}
#ifdef DEBUG
state_ = State::IncDec;
#endif
return true;
}

View file

@ -0,0 +1,278 @@
/* -*- 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/. */
#ifndef frontend_ElemOpEmitter_h
#define frontend_ElemOpEmitter_h
#include "mozilla/Attributes.h"
namespace js {
namespace frontend {
struct BytecodeEmitter;
// Class for emitting bytecode for element operation.
//
// Usage: (check for the return value is omitted for simplicity)
//
// `obj[key];`
// ElemOpEmitter eoe(this,
// ElemOpEmitter::Kind::Get,
// ElemOpEmitter::ObjKind::Other);
// eoe.prepareForObj();
// emit(obj);
// eoe.prepareForKey();
// emit(key);
// eoe.emitGet();
//
// `super[key];`
// ElemOpEmitter eoe(this,
// ElemOpEmitter::Kind::Get,
// ElemOpEmitter::ObjKind::Super);
// eoe.prepareForObj();
// emit(this_for_super);
// eoe.prepareForKey();
// emit(key);
// eoe.emitGet();
//
// `obj[key]();`
// ElemOpEmitter eoe(this,
// ElemOpEmitter::Kind::Call,
// ElemOpEmitter::ObjKind::Other);
// eoe.prepareForObj();
// emit(obj);
// eoe.prepareForKey();
// emit(key);
// eoe.emitGet();
// emit_call_here();
//
// `new obj[key]();`
// ElemOpEmitter eoe(this,
// ElemOpEmitter::Kind::Call,
// ElemOpEmitter::ObjKind::Other);
// eoe.prepareForObj();
// emit(obj);
// eoe.prepareForKey();
// emit(key);
// eoe.emitGet();
// emit_call_here();
//
// `delete obj[key];`
// ElemOpEmitter eoe(this,
// ElemOpEmitter::Kind::Delete,
// ElemOpEmitter::ObjKind::Other);
// eoe.prepareForObj();
// emit(obj);
// eoe.prepareForKey();
// emit(key);
// eoe.emitDelete();
//
// `delete super[key];`
// ElemOpEmitter eoe(this,
// ElemOpEmitter::Kind::Delete,
// ElemOpEmitter::ObjKind::Super);
// eoe.prepareForObj();
// emit(this_for_super);
// eoe.prepareForKey();
// emit(key);
// eoe.emitDelete();
//
// `obj[key]++;`
// ElemOpEmitter eoe(this,
// ElemOpEmitter::Kind::PostIncrement,
// ElemOpEmitter::ObjKind::Other);
// eoe.prepareForObj();
// emit(obj);
// eoe.prepareForKey();
// emit(key);
// eoe.emitIncDec();
//
// `obj[key] = value;`
// ElemOpEmitter eoe(this,
// ElemOpEmitter::Kind::SimpleAssignment,
// ElemOpEmitter::ObjKind::Other);
// eoe.prepareForObj();
// emit(obj);
// eoe.prepareForKey();
// emit(key);
// eoe.prepareForRhs();
// emit(value);
// eoe.emitAssignment();
//
// `obj[key] += value;`
// ElemOpEmitter eoe(this,
// ElemOpEmitter::Kind::CompoundAssignment,
// ElemOpEmitter::ObjKind::Other);
// eoe.prepareForObj();
// emit(obj);
// eoe.prepareForKey();
// emit(key);
// eoe.emitGet();
// eoe.prepareForRhs();
// emit(value);
// emit_add_op_here();
// eoe.emitAssignment();
//
class MOZ_STACK_CLASS ElemOpEmitter
{
public:
enum class Kind {
Get,
Call,
Set,
Delete,
PostIncrement,
PreIncrement,
PostDecrement,
PreDecrement,
SimpleAssignment,
CompoundAssignment
};
enum class ObjKind {
Super,
Other
};
private:
BytecodeEmitter* bce_;
Kind kind_;
ObjKind objKind_;
#ifdef DEBUG
// The state of this emitter.
//
// skipObjAndKeyAndRhs
// +------------------------------------------------+
// | |
// +-------+ | prepareForObj +-----+ prepareForKey +-----+ |
// | Start |-+-------------->| Obj |-------------->| Key |-+ |
// +-------+ +-----+ +-----+ | |
// | |
// +-------------------------------------------------------+ |
// | |
// | |
// | |
// | [Get] |
// | [Call] |
// | emitGet +-----+ |
// +---------->| Get | |
// | +-----+ |
// | |
// | [Delete] |
// | emitDelete +--------+ |
// +------------->| Delete | |
// | +--------+ |
// | |
// | [PostIncrement] |
// | [PreIncrement] |
// | [PostDecrement] |
// | [PreDecrement] |
// | emitIncDec +--------+ |
// +------------->| IncDec | |
// | +--------+ |
// | +-------------------+
// | [SimpleAssignment] |
// | prepareForRhs v +-----+
// +--------------------->+-------------->+->| Rhs |-+
// | ^ +-----+ |
// | | |
// | | +-------------+
// | [CompoundAssignment] | |
// | emitGet +-----+ | | emitAssignment +------------+
// +---------->| Get |----+ +--------------->| Assignment |
// +-----+ +------------+
enum class State {
// The initial state.
Start,
// After calling prepareForObj.
Obj,
// After calling emitKey.
Key,
// After calling emitGet.
Get,
// After calling emitDelete.
Delete,
// After calling emitIncDec.
IncDec,
// After calling prepareForRhs or skipObjAndKeyAndRhs.
Rhs,
// After calling emitAssignment.
Assignment,
};
State state_ = State::Start;
#endif
public:
ElemOpEmitter(BytecodeEmitter* bce, Kind kind, ObjKind objKind);
private:
MOZ_MUST_USE bool isCall() const {
return kind_ == Kind::Call;
}
MOZ_MUST_USE bool isSimpleAssignment() const {
return kind_ == Kind::SimpleAssignment;
}
MOZ_MUST_USE bool isDelete() const {
return kind_ == Kind::Delete;
}
MOZ_MUST_USE bool isCompoundAssignment() const {
return kind_ == Kind::CompoundAssignment;
}
MOZ_MUST_USE bool isIncDec() const {
return isPostIncDec() || isPreIncDec();
}
MOZ_MUST_USE bool isPostIncDec() const {
return kind_ == Kind::PostIncrement ||
kind_ == Kind::PostDecrement;
}
MOZ_MUST_USE bool isPreIncDec() const {
return kind_ == Kind::PreIncrement ||
kind_ == Kind::PreDecrement;
}
MOZ_MUST_USE bool isInc() const {
return kind_ == Kind::PostIncrement ||
kind_ == Kind::PreIncrement;
}
MOZ_MUST_USE bool isSuper() const {
return objKind_ == ObjKind::Super;
}
public:
MOZ_MUST_USE bool prepareForObj();
MOZ_MUST_USE bool prepareForKey();
MOZ_MUST_USE bool emitGet();
MOZ_MUST_USE bool prepareForRhs();
MOZ_MUST_USE bool skipObjAndKeyAndRhs();
MOZ_MUST_USE bool emitDelete();
MOZ_MUST_USE bool emitAssignment();
MOZ_MUST_USE bool emitIncDec();
};
} /* namespace frontend */
} /* namespace js */
#endif /* frontend_ElemOpEmitter_h */

View file

@ -0,0 +1,382 @@
/* -*- 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/. */
#include "frontend/NameOpEmitter.h"
#include "frontend/BytecodeEmitter.h"
#include "frontend/SharedContext.h"
#include "frontend/TDZCheckCache.h"
#include "vm/Stack.h" // for MaybeCheckTDZ
#include "vm/Opcodes.h"
#include "vm/Scope.h"
#include "vm/String.h"
using namespace js;
using namespace js::frontend;
NameOpEmitter::NameOpEmitter(BytecodeEmitter* bce, JSAtom* name, Kind kind)
: bce_(bce),
kind_(kind),
name_(bce_->cx, name),
loc_(bce_->lookupName(name_))
{}
NameOpEmitter::NameOpEmitter(BytecodeEmitter* bce, JSAtom* name, const NameLocation& loc,
Kind kind)
: bce_(bce),
kind_(kind),
name_(bce_->cx, name),
loc_(loc)
{}
bool
NameOpEmitter::emitGet()
{
MOZ_ASSERT(state_ == State::Start);
switch (loc_.kind()) {
case NameLocation::Kind::Dynamic:
if (!bce_->emitAtomOp(name_, JSOP_GETNAME)) { // VAL
return false;
}
break;
case NameLocation::Kind::Global:
if (!bce_->emitAtomOp(name_, JSOP_GETGNAME)) {// VAL
return false;
}
break;
case NameLocation::Kind::Intrinsic:
if (!bce_->emitAtomOp(name_, JSOP_GETINTRINSIC)) {
return false; // VAL
}
break;
case NameLocation::Kind::NamedLambdaCallee:
if (!bce_->emit1(JSOP_CALLEE)) { // VAL
return false;
}
break;
case NameLocation::Kind::Import:
if (!bce_->emitAtomOp(name_, JSOP_GETIMPORT)) {
return false; // VAL
}
break;
case NameLocation::Kind::ArgumentSlot:
if (!bce_->emitArgOp(JSOP_GETARG, loc_.argumentSlot())) {
return false; // VAL
}
break;
case NameLocation::Kind::FrameSlot:
if (loc_.isLexical()) {
if (!bce_->emitTDZCheckIfNeeded(name_, loc_)) {
return false;
}
}
if (!bce_->emitLocalOp(JSOP_GETLOCAL, loc_.frameSlot())) {
return false; // VAL
}
break;
case NameLocation::Kind::EnvironmentCoordinate:
if (loc_.isLexical()) {
if (!bce_->emitTDZCheckIfNeeded(name_, loc_)) {
return false;
}
}
if (!bce_->emitEnvCoordOp(JSOP_GETALIASEDVAR, loc_.environmentCoordinate())) {
return false; // VAL
}
break;
case NameLocation::Kind::DynamicAnnexBVar:
MOZ_CRASH("Synthesized vars for Annex B.3.3 should only be used in initialization");
}
if (isCall()) {
switch (loc_.kind()) {
case NameLocation::Kind::Dynamic: {
JSOp thisOp = bce_->needsImplicitThis() ? JSOP_IMPLICITTHIS : JSOP_GIMPLICITTHIS;
if (!bce_->emitAtomOp(name_, thisOp)) { // CALLEE THIS
return false;
}
break;
}
case NameLocation::Kind::Global:
if (!bce_->emitAtomOp(name_, JSOP_GIMPLICITTHIS)) {
return false; // CALLEE THIS
}
break;
case NameLocation::Kind::Intrinsic:
case NameLocation::Kind::NamedLambdaCallee:
case NameLocation::Kind::Import:
case NameLocation::Kind::ArgumentSlot:
case NameLocation::Kind::FrameSlot:
case NameLocation::Kind::EnvironmentCoordinate:
if (!bce_->emit1(JSOP_UNDEFINED)) { // CALLEE UNDEF
return false;
}
break;
case NameLocation::Kind::DynamicAnnexBVar:
MOZ_CRASH("Synthesized vars for Annex B.3.3 should only be used in initialization");
}
}
#ifdef DEBUG
state_ = State::Get;
#endif
return true;
}
bool
NameOpEmitter::prepareForRhs()
{
MOZ_ASSERT(state_ == State::Start);
switch (loc_.kind()) {
case NameLocation::Kind::Dynamic:
case NameLocation::Kind::Import:
case NameLocation::Kind::DynamicAnnexBVar:
if (!bce_->makeAtomIndex(name_, &atomIndex_)) {
return false;
}
if (loc_.kind() == NameLocation::Kind::DynamicAnnexBVar) {
// Annex B vars always go on the nearest variable environment,
// even if lexical environments in between contain same-named
// bindings.
if (!bce_->emit1(JSOP_BINDVAR)) { // ENV
return false;
}
} else {
if (!bce_->emitIndexOp(JSOP_BINDNAME, atomIndex_)) {
return false; // ENV
}
}
emittedBindOp_ = true;
break;
case NameLocation::Kind::Global:
if (!bce_->makeAtomIndex(name_, &atomIndex_)) {
return false;
}
if (loc_.isLexical() && isInitialize()) {
// INITGLEXICAL always gets the global lexical scope. It doesn't
// need a BINDGNAME.
MOZ_ASSERT(bce_->innermostScope()->is<GlobalScope>());
} else {
if (!bce_->emitIndexOp(JSOP_BINDGNAME, atomIndex_)) {
return false; // ENV
}
emittedBindOp_ = true;
}
break;
case NameLocation::Kind::Intrinsic:
break;
case NameLocation::Kind::NamedLambdaCallee:
break;
case NameLocation::Kind::ArgumentSlot: {
// If we assign to a positional formal parameter and the arguments
// object is unmapped (strict mode or function with
// default/rest/destructing args), parameters do not alias
// arguments[i], and to make the arguments object reflect initial
// parameter values prior to any mutation we create it eagerly
// whenever parameters are (or might, in the case of calls to eval)
// assigned.
FunctionBox* funbox = bce_->sc->asFunctionBox();
if (funbox->argumentsHasLocalBinding() && !funbox->hasMappedArgsObj()) {
funbox->setDefinitelyNeedsArgsObj();
}
break;
}
case NameLocation::Kind::FrameSlot:
break;
case NameLocation::Kind::EnvironmentCoordinate:
break;
}
// For compound assignments, first get the LHS value, then emit
// the RHS and the op.
if (isCompoundAssignment() || isIncDec()) {
if (loc_.kind() == NameLocation::Kind::Dynamic) {
// For dynamic accesses we need to emit GETBOUNDNAME instead of
// GETNAME for correctness: looking up @@unscopables on the
// environment chain (due to 'with' environments) must only happen
// once.
//
// GETBOUNDNAME uses the environment already pushed on the stack
// from the earlier BINDNAME.
if (!bce_->emit1(JSOP_DUP)) { // ENV ENV
return false;
}
if (!bce_->emitAtomOp(name_, JSOP_GETXPROP)) {
return false; // ENV V
}
} else {
if (!emitGet()) { // ENV? V
return false;
}
}
}
#ifdef DEBUG
state_ = State::Rhs;
#endif
return true;
}
bool
NameOpEmitter::emitAssignment()
{
MOZ_ASSERT(state_ == State::Rhs);
switch (loc_.kind()) {
case NameLocation::Kind::Dynamic:
case NameLocation::Kind::Import:
case NameLocation::Kind::DynamicAnnexBVar:
if (!bce_->emitIndexOp(bce_->strictifySetNameOp(JSOP_SETNAME), atomIndex_)) {
return false;
}
break;
case NameLocation::Kind::Global: {
JSOp op;
if (emittedBindOp_) {
op = bce_->strictifySetNameOp(JSOP_SETGNAME);
} else {
op = JSOP_INITGLEXICAL;
}
if (!bce_->emitIndexOp(op, atomIndex_)) {
return false;
}
break;
}
case NameLocation::Kind::Intrinsic:
if (!bce_->emitAtomOp(name_, JSOP_SETINTRINSIC)) {
return false;
}
break;
case NameLocation::Kind::NamedLambdaCallee:
// Assigning to the named lambda is a no-op in sloppy mode but
// throws in strict mode.
if (bce_->sc->strict()) {
if (!bce_->emit1(JSOP_THROWSETCALLEE)) {
return false;
}
}
break;
case NameLocation::Kind::ArgumentSlot:
if (!bce_->emitArgOp(JSOP_SETARG, loc_.argumentSlot())) {
return false;
}
break;
case NameLocation::Kind::FrameSlot: {
JSOp op = JSOP_SETLOCAL;
if (loc_.isLexical()) {
if (isInitialize()) {
op = JSOP_INITLEXICAL;
} else {
if (loc_.isConst()) {
op = JSOP_THROWSETCONST;
}
if (!bce_->emitTDZCheckIfNeeded(name_, loc_)) {
return false;
}
}
}
if (!bce_->emitLocalOp(op, loc_.frameSlot())) {
return false;
}
if (op == JSOP_INITLEXICAL) {
if (!bce_->innermostTDZCheckCache->noteTDZCheck(bce_, name_, DontCheckTDZ)) {
return false;
}
}
break;
}
case NameLocation::Kind::EnvironmentCoordinate: {
JSOp op = JSOP_SETALIASEDVAR;
if (loc_.isLexical()) {
if (isInitialize()) {
op = JSOP_INITALIASEDLEXICAL;
} else {
if (loc_.isConst()) {
op = JSOP_THROWSETALIASEDCONST;
}
if (!bce_->emitTDZCheckIfNeeded(name_, loc_)) {
return false;
}
}
}
if (loc_.bindingKind() == BindingKind::NamedLambdaCallee) {
// Assigning to the named lambda is a no-op in sloppy mode and throws
// in strict mode.
op = JSOP_THROWSETALIASEDCONST;
if (bce_->sc->strict()) {
if (!bce_->emitEnvCoordOp(op, loc_.environmentCoordinate())) {
return false;
}
}
} else {
if (!bce_->emitEnvCoordOp(op, loc_.environmentCoordinate())) {
return false;
}
}
if (op == JSOP_INITALIASEDLEXICAL) {
if (!bce_->innermostTDZCheckCache->noteTDZCheck(bce_, name_, DontCheckTDZ)) {
return false;
}
}
break;
}
}
#ifdef DEBUG
state_ = State::Assignment;
#endif
return true;
}
bool
NameOpEmitter::emitIncDec()
{
MOZ_ASSERT(state_ == State::Start);
JSOp binOp = isInc() ? JSOP_ADD : JSOP_SUB;
if (!prepareForRhs()) { // ENV? V
return false;
}
if (!bce_->emit1(JSOP_POS)) { // ENV? N
return false;
}
if (isPostIncDec()) {
if (!bce_->emit1(JSOP_DUP)) { // ENV? N? N
return false;
}
}
if (!bce_->emit1(JSOP_ONE)) { // ENV? N? N 1
return false;
}
if (!bce_->emit1(binOp)) { // ENV? N? N+1
return false;
}
if (isPostIncDec() && emittedBindOp()) {
if (!bce_->emit2(JSOP_PICK, 2)) { // N? N+1 ENV?
return false;
}
if (!bce_->emit1(JSOP_SWAP)) { // N? ENV? N+1
return false;
}
}
if (!emitAssignment()) { // N? N+1
return false;
}
if (isPostIncDec()) {
if (!bce_->emit1(JSOP_POP)) { // N
return false;
}
}
#ifdef DEBUG
state_ = State::IncDec;
#endif
return true;
}

View file

@ -0,0 +1,193 @@
/* -*- 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/. */
#ifndef frontend_NameOpEmitter_h
#define frontend_NameOpEmitter_h
#include "mozilla/Attributes.h"
#include <stdint.h>
#include "frontend/NameAnalysisTypes.h"
#include "js/TypeDecls.h"
namespace js {
namespace frontend {
struct BytecodeEmitter;
// Class for emitting bytecode for name operation.
//
// Usage: (check for the return value is omitted for simplicity)
//
// `name;`
// NameOpEmitter noe(this, atom_of_name
// ElemOpEmitter::Kind::Get);
// noe.emitGet();
//
// `name();`
// this is handled in CallOrNewEmitter
//
// `name++;`
// NameOpEmitter noe(this, atom_of_name
// ElemOpEmitter::Kind::PostIncrement);
// noe.emitIncDec();
//
// `name = 10;`
// NameOpEmitter noe(this, atom_of_name
// ElemOpEmitter::Kind::SimpleAssignment);
// noe.prepareForRhs();
// emit(10);
// noe.emitAssignment();
//
// `name += 10;`
// NameOpEmitter noe(this, atom_of_name
// ElemOpEmitter::Kind::CompoundAssignment);
// noe.prepareForRhs();
// emit(10);
// emit_add_op_here();
// noe.emitAssignment();
//
// `name = 10;` part of `let name = 10;`
// NameOpEmitter noe(this, atom_of_name
// ElemOpEmitter::Kind::Initialize);
// noe.prepareForRhs();
// emit(10);
// noe.emitAssignment();
//
class MOZ_STACK_CLASS NameOpEmitter
{
public:
enum class Kind {
Get,
Call,
PostIncrement,
PreIncrement,
PostDecrement,
PreDecrement,
SimpleAssignment,
CompoundAssignment,
Initialize
};
private:
BytecodeEmitter* bce_;
Kind kind_;
bool emittedBindOp_ = false;
RootedAtom name_;
uint32_t atomIndex_;
NameLocation loc_;
#ifdef DEBUG
// The state of this emitter.
//
// [Get]
// [Call]
// +-------+ emitGet +-----+
// | Start |-+-+--------->| Get |
// +-------+ | +-----+
// |
// | [PostIncrement]
// | [PreIncrement]
// | [PostDecrement]
// | [PreDecrement]
// | emitIncDec +--------+
// +------------->| IncDec |
// | +--------+
// |
// | [SimpleAssignment]
// | prepareForRhs +-----+
// +--------------------->+-------------->| Rhs |-+
// | ^ +-----+ |
// | | |
// | | +------------------+
// | [CompoundAssignment] | |
// | emitGet +-----+ | | emitAssignment +------------+
// +---------->| Get |----+ + -------------->| Assignment |
// +-----+ +------------+
enum class State {
// The initial state.
Start,
// After calling emitGet.
Get,
// After calling emitIncDec.
IncDec,
// After calling prepareForRhs.
Rhs,
// After calling emitAssignment.
Assignment,
};
State state_ = State::Start;
#endif
public:
NameOpEmitter(BytecodeEmitter* bce, JSAtom* name, Kind kind);
NameOpEmitter(BytecodeEmitter* bce, JSAtom* name, const NameLocation& loc, Kind kind);
private:
MOZ_MUST_USE bool isCall() const {
return kind_ == Kind::Call;
}
MOZ_MUST_USE bool isSimpleAssignment() const {
return kind_ == Kind::SimpleAssignment;
}
MOZ_MUST_USE bool isCompoundAssignment() const {
return kind_ == Kind::CompoundAssignment;
}
MOZ_MUST_USE bool isIncDec() const {
return isPostIncDec() || isPreIncDec();
}
MOZ_MUST_USE bool isPostIncDec() const {
return kind_ == Kind::PostIncrement ||
kind_ == Kind::PostDecrement;
}
MOZ_MUST_USE bool isPreIncDec() const {
return kind_ == Kind::PreIncrement ||
kind_ == Kind::PreDecrement;
}
MOZ_MUST_USE bool isInc() const {
return kind_ == Kind::PostIncrement ||
kind_ == Kind::PreIncrement;
}
MOZ_MUST_USE bool isInitialize() const {
return kind_ == Kind::Initialize;
}
public:
MOZ_MUST_USE bool emittedBindOp() const {
return emittedBindOp_;
}
MOZ_MUST_USE const NameLocation& loc() const {
return loc_;
}
MOZ_MUST_USE bool emitGet();
MOZ_MUST_USE bool prepareForRhs();
MOZ_MUST_USE bool emitAssignment();
MOZ_MUST_USE bool emitIncDec();
};
} /* namespace frontend */
} /* namespace js */
#endif /* frontend_NameOpEmitter_h */

View file

@ -1248,6 +1248,10 @@ class PropertyAccessBase : public ParseNode
PropertyName& name() const {
return *pn_u.name.atom->asPropertyName();
}
JSAtom* nameAtom() const {
return pn_u.name.atom;
}
};
class PropertyAccess : public PropertyAccessBase
@ -1302,6 +1306,14 @@ class PropertyByValueBase : public ParseNode
pn_u.binary.right = propExpr;
}
ParseNode& expression() const {
return *pn_u.binary.left;
}
ParseNode& key() const {
return *pn_u.binary.right;
}
static bool test(const ParseNode& node) {
bool match = node.isKind(PNK_ELEM) ||
node.isKind(PNK_OPTELEM);

View file

@ -0,0 +1,275 @@
/* -*- 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/. */
#include "frontend/PropOpEmitter.h"
#include "frontend/BytecodeEmitter.h"
#include "frontend/SharedContext.h"
#include "vm/Opcodes.h"
#include "vm/String.h"
using namespace js;
using namespace js::frontend;
PropOpEmitter::PropOpEmitter(BytecodeEmitter* bce, Kind kind, ObjKind objKind)
: bce_(bce),
kind_(kind),
objKind_(objKind)
{}
bool
PropOpEmitter::prepareAtomIndex(JSAtom* prop)
{
if (!bce_->makeAtomIndex(prop, &propAtomIndex_)) {
return false;
}
isLength_ = prop == bce_->cx->names().length;
return true;
}
bool
PropOpEmitter::prepareForObj()
{
MOZ_ASSERT(state_ == State::Start);
#ifdef DEBUG
state_ = State::Obj;
#endif
return true;
}
bool
PropOpEmitter::emitGet(JSAtom* prop)
{
MOZ_ASSERT(state_ == State::Obj);
if (!prepareAtomIndex(prop)) {
return false;
}
if (isCall()) {
if (!bce_->emit1(JSOP_DUP)) { // [Super]
// // THIS THIS
// // [Other]
// // OBJ OBJ
return false;
}
}
if (isSuper()) {
if (!bce_->emit1(JSOP_SUPERBASE)) { // THIS? THIS SUPERBASE
return false;
}
}
if (isIncDec() || isCompoundAssignment()) {
if (isSuper()) {
if (!bce_->emit1(JSOP_DUP2)) { // THIS SUPERBASE THIS SUPERBASE
return false;
}
} else {
if (!bce_->emit1(JSOP_DUP)) { // OBJ OBJ
return false;
}
}
}
JSOp op;
if (isSuper()) {
op = JSOP_GETPROP_SUPER;
} else if (isCall()) {
op = JSOP_CALLPROP;
} else {
op = isLength_ ? JSOP_LENGTH : JSOP_GETPROP;
}
if (!bce_->emitAtomOp(propAtomIndex_, op)) { // [Get]
// // PROP
// // [Call]
// // THIS PROP
// // [Inc/Dec/Compound,
// // Super]
// // THIS SUPERBASE PROP
// // [Inc/Dec/Compound,
// // Other]
// // OBJ PROP
return false;
}
if (isCall()) {
if (!bce_->emit1(JSOP_SWAP)) { // PROP THIS
return false;
}
}
#ifdef DEBUG
state_ = State::Get;
#endif
return true;
}
bool
PropOpEmitter::prepareForRhs()
{
MOZ_ASSERT(isSimpleAssignment() || isCompoundAssignment());
MOZ_ASSERT_IF(isSimpleAssignment(), state_ == State::Obj);
MOZ_ASSERT_IF(isCompoundAssignment(), state_ == State::Get);
if (isSimpleAssignment()) {
// For CompoundAssignment, SUPERBASE is already emitted by emitGet.
if (isSuper()) {
if (!bce_->emit1(JSOP_SUPERBASE)) { // THIS SUPERBASE
return false;
}
}
}
#ifdef DEBUG
state_ = State::Rhs;
#endif
return true;
}
bool
PropOpEmitter::skipObjAndRhs()
{
MOZ_ASSERT(state_ == State::Start);
MOZ_ASSERT(isSimpleAssignment());
#ifdef DEBUG
state_ = State::Rhs;
#endif
return true;
}
bool
PropOpEmitter::emitDelete(JSAtom* prop)
{
MOZ_ASSERT_IF(!isSuper(), state_ == State::Obj);
MOZ_ASSERT_IF(isSuper(), state_ == State::Start);
MOZ_ASSERT(isDelete());
if (!prepareAtomIndex(prop)) {
return false;
}
if (isSuper()) {
if (!bce_->emit1(JSOP_SUPERBASE)) { // THIS SUPERBASE
return false;
}
// Unconditionally throw when attempting to delete a super-reference.
if (!bce_->emitUint16Operand(JSOP_THROWMSG, JSMSG_CANT_DELETE_SUPER)) {
return false; // THIS SUPERBASE
}
// Another wrinkle: Balance the stack from the emitter's point of view.
// Execution will not reach here, as the last bytecode threw.
if (!bce_->emit1(JSOP_POP)) { // THIS
return false;
}
} else {
JSOp op = bce_->sc->strict() ? JSOP_STRICTDELPROP : JSOP_DELPROP;
if (!bce_->emitAtomOp(propAtomIndex_, op)) { // SUCCEEDED
return false;
}
}
#ifdef DEBUG
state_ = State::Delete;
#endif
return true;
}
bool
PropOpEmitter::emitAssignment(JSAtom* prop)
{
MOZ_ASSERT(isSimpleAssignment() || isCompoundAssignment());
MOZ_ASSERT(state_ == State::Rhs);
if (isSimpleAssignment()) {
if (!prepareAtomIndex(prop)) {
return false;
}
}
JSOp setOp = isSuper()
? bce_->sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER
: bce_->sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP;
if (!bce_->emitAtomOp(propAtomIndex_, setOp)) { // VAL
return false;
}
#ifdef DEBUG
state_ = State::Assignment;
#endif
return true;
}
bool
PropOpEmitter::emitIncDec(JSAtom* prop)
{
MOZ_ASSERT(state_ == State::Obj);
MOZ_ASSERT(isIncDec());
if (!emitGet(prop)) {
return false;
}
MOZ_ASSERT(state_ == State::Get);
JSOp binOp = isInc() ? JSOP_ADD : JSOP_SUB;
if (!bce_->emit1(JSOP_POS)) { // ... N
return false;
}
if (isPostIncDec()) {
if (!bce_->emit1(JSOP_DUP)) { // ... N N
return false;
}
}
if (!bce_->emit1(JSOP_ONE)) { // ... N? N 1
return false;
}
if (!bce_->emit1(binOp)) { // ... N? N+1
return false;
}
if (isPostIncDec()) {
if (isSuper()) { // THIS OBJ N N+1
if (!bce_->emit2(JSOP_PICK, 3)) { // OBJ N N+1 THIS
return false;
}
if (!bce_->emit1(JSOP_SWAP)) { // OBJ N THIS N+1
return false;
}
if (!bce_->emit2(JSOP_PICK, 3)) { // N THIS N+1 OBJ
return false;
}
if (!bce_->emit1(JSOP_SWAP)) { // N THIS OBJ N+1
return false;
}
} else { // OBJ N N+1
if (!bce_->emit2(JSOP_PICK, 2)) { // N N+1 OBJ
return false;
}
if (!bce_->emit1(JSOP_SWAP)) { // N OBJ N+1
return false;
}
}
}
JSOp setOp = isSuper()
? bce_->sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER
: bce_->sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP;
if (!bce_->emitAtomOp(propAtomIndex_, setOp)) { // N? N+1
return false;
}
if (isPostIncDec()) {
if (!bce_->emit1(JSOP_POP)) { // N
return false;
}
}
#ifdef DEBUG
state_ = State::IncDec;
#endif
return true;
}

View file

@ -0,0 +1,269 @@
/* -*- 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/. */
#ifndef frontend_PropOpEmitter_h
#define frontend_PropOpEmitter_h
#include "mozilla/Attributes.h"
#include <stdint.h>
#include "js/TypeDecls.h"
class JSAtom;
namespace js {
namespace frontend {
struct BytecodeEmitter;
// Class for emitting bytecode for property operation.
//
// Usage: (check for the return value is omitted for simplicity)
//
// `obj.prop;`
// PropOpEmitter poe(this,
// PropOpEmitter::Kind::Get,
// PropOpEmitter::ObjKind::Other);
// poe.prepareForObj();
// emit(obj);
// poe.emitGet(atom_of_prop);
//
// `super.prop;`
// PropOpEmitter poe(this,
// PropOpEmitter::Kind::Get,
// PropOpEmitter::ObjKind::Super);
// poe.prepareForObj();
// emit(obj);
// poe.emitGet(atom_of_prop);
//
// `obj.prop();`
// PropOpEmitter poe(this,
// PropOpEmitter::Kind::Call,
// PropOpEmitter::ObjKind::Other);
// poe.prepareForObj();
// emit(obj);
// poe.emitGet(atom_of_prop);
// emit_call_here();
//
// `new obj.prop();`
// PropOpEmitter poe(this,
// PropOpEmitter::Kind::Call,
// PropOpEmitter::ObjKind::Other);
// poe.prepareForObj();
// emit(obj);
// poe.emitGet(atom_of_prop);
// emit_call_here();
//
// `delete obj.prop;`
// PropOpEmitter poe(this,
// PropOpEmitter::Kind::Delete,
// PropOpEmitter::ObjKind::Other);
// poe.prepareForObj();
// emit(obj);
// poe.emitDelete(atom_of_prop);
//
// `delete super.prop;`
// PropOpEmitter poe(this,
// PropOpEmitter::Kind::Delete,
// PropOpEmitter::ObjKind::Other);
// poe.emitDelete(atom_of_prop);
//
// `obj.prop++;`
// PropOpEmitter poe(this,
// PropOpEmitter::Kind::PostIncrement,
// PropOpEmitter::ObjKind::Other);
// poe.prepareForObj();
// emit(obj);
// poe.emitIncDec(atom_of_prop);
//
// `obj.prop = value;`
// PropOpEmitter poe(this,
// PropOpEmitter::Kind::SimpleAssignment,
// PropOpEmitter::ObjKind::Other);
// poe.prepareForObj();
// emit(obj);
// poe.prepareForRhs();
// emit(value);
// poe.emitAssignment(atom_of_prop);
//
// `obj.prop += value;`
// PropOpEmitter poe(this,
// PropOpEmitter::Kind::CompoundAssignment,
// PropOpEmitter::ObjKind::Other);
// poe.prepareForObj();
// emit(obj);
// poe.emitGet(atom_of_prop);
// poe.prepareForRhs();
// emit(value);
// emit_add_op_here();
// poe.emitAssignment(nullptr); // nullptr for CompoundAssignment
//
class MOZ_STACK_CLASS PropOpEmitter
{
public:
enum class Kind {
Get,
Call,
Set,
Delete,
PostIncrement,
PreIncrement,
PostDecrement,
PreDecrement,
SimpleAssignment,
CompoundAssignment
};
enum class ObjKind {
Super,
Other
};
private:
BytecodeEmitter* bce_;
Kind kind_;
ObjKind objKind_;
// The index for the property name's atom.
uint32_t propAtomIndex_ = 0;
// Whether the property name is `length` or not.
bool isLength_ = false;
#ifdef DEBUG
// The state of this emitter.
//
// skipObjAndRhs
// +----------------------------+
// | |
// +-------+ | prepareForObj +-----+ |
// | Start |-+-------------->| Obj |-+ |
// +-------+ +-----+ | |
// | |
// +---------------------------------+ |
// | |
// | |
// | [Get] |
// | [Call] |
// | emitGet +-----+ |
// +---------->| Get | |
// | +-----+ |
// | |
// | [Delete] |
// | emitDelete +--------+ |
// +------------->| Delete | |
// | +--------+ |
// | |
// | [PostIncrement] |
// | [PreIncrement] |
// | [PostDecrement] |
// | [PreDecrement] |
// | emitIncDec +--------+ |
// +------------->| IncDec | |
// | +--------+ |
// | |
// | [SimpleAssignment] |
// | prepareForRhs | +-----+
// +--------------------->+-------------->+->| Rhs |-+
// | ^ +-----+ |
// | | |
// | | +---------+
// | [CompoundAssignment] | |
// | emitGet +-----+ | | emitAssignment +------------+
// +---------->| Get |----+ + -------------->| Assignment |
// +-----+ +------------+
enum class State {
// The initial state.
Start,
// After calling prepareForObj.
Obj,
// After calling emitGet.
Get,
// After calling emitDelete.
Delete,
// After calling emitIncDec.
IncDec,
// After calling prepareForRhs or skipObjAndRhs.
Rhs,
// After calling emitAssignment.
Assignment,
};
State state_ = State::Start;
#endif
public:
PropOpEmitter(BytecodeEmitter* bce, Kind kind, ObjKind objKind);
private:
MOZ_MUST_USE bool isCall() const {
return kind_ == Kind::Call;
}
MOZ_MUST_USE bool isSuper() const {
return objKind_ == ObjKind::Super;
}
MOZ_MUST_USE bool isSimpleAssignment() const {
return kind_ == Kind::SimpleAssignment;
}
MOZ_MUST_USE bool isDelete() const {
return kind_ == Kind::Delete;
}
MOZ_MUST_USE bool isCompoundAssignment() const {
return kind_ == Kind::CompoundAssignment;
}
MOZ_MUST_USE bool isIncDec() const {
return isPostIncDec() || isPreIncDec();
}
MOZ_MUST_USE bool isPostIncDec() const {
return kind_ == Kind::PostIncrement ||
kind_ == Kind::PostDecrement;
}
MOZ_MUST_USE bool isPreIncDec() const {
return kind_ == Kind::PreIncrement ||
kind_ == Kind::PreDecrement;
}
MOZ_MUST_USE bool isInc() const {
return kind_ == Kind::PostIncrement ||
kind_ == Kind::PreIncrement;
}
MOZ_MUST_USE bool
prepareAtomIndex(JSAtom* prop);
public:
MOZ_MUST_USE bool prepareForObj();
MOZ_MUST_USE bool emitGet(JSAtom* prop);
MOZ_MUST_USE bool prepareForRhs();
MOZ_MUST_USE bool skipObjAndRhs();
MOZ_MUST_USE bool emitDelete(JSAtom* prop);
// `prop` can be nullptr for CompoundAssignment.
MOZ_MUST_USE bool emitAssignment(JSAtom* prop);
MOZ_MUST_USE bool emitIncDec(JSAtom* prop);
};
} /* namespace frontend */
} /* namespace js */
#endif /* frontend_PropOpEmitter_h */

View file

@ -0,0 +1,28 @@
/* -*- 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/. */
#ifndef frontend_ValueUsage_h
#define frontend_ValueUsage_h
namespace js {
namespace frontend {
// Used to control whether JSOP_CALL_IGNORES_RV is emitted for function calls.
enum class ValueUsage {
// Assume the value of the current expression may be used. This is always
// correct but prohibits JSOP_CALL_IGNORES_RV.
WantValue,
// Pass this when emitting an expression if the expression's value is
// definitely unused by later instructions. You must make sure the next
// instruction is JSOP_POP, a jump to a JSOP_POP, or something similar.
IgnoreValue
};
} /* namespace frontend */
} /* namespace js */
#endif /* frontend_ValueUsage_h */

View file

@ -140,11 +140,15 @@ UNIFIED_SOURCES += [
'ds/MemoryProtectionExceptionHandler.cpp',
'frontend/BytecodeCompiler.cpp',
'frontend/BytecodeEmitter.cpp',
'frontend/CallOrNewEmitter.cpp',
'frontend/ElemOpEmitter.cpp',
'frontend/FoldConstants.cpp',
'frontend/IfEmitter.cpp',
'frontend/JumpList.cpp',
'frontend/NameFunctions.cpp',
'frontend/NameOpEmitter.cpp',
'frontend/ParseNode.cpp',
'frontend/PropOpEmitter.cpp',
'frontend/TDZCheckCache.cpp',
'frontend/TokenStream.cpp',
'gc/Allocator.cpp',