mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-05 23:38:38 +09:00
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:
parent
a2fd8846b6
commit
89e6820006
13 changed files with 3240 additions and 1092 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -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);
|
||||
|
|
|
|||
319
js/src/frontend/CallOrNewEmitter.cpp
Normal file
319
js/src/frontend/CallOrNewEmitter.cpp
Normal 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;
|
||||
}
|
||||
338
js/src/frontend/CallOrNewEmitter.h
Normal file
338
js/src/frontend/CallOrNewEmitter.h
Normal 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 */
|
||||
288
js/src/frontend/ElemOpEmitter.cpp
Normal file
288
js/src/frontend/ElemOpEmitter.cpp
Normal 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;
|
||||
}
|
||||
278
js/src/frontend/ElemOpEmitter.h
Normal file
278
js/src/frontend/ElemOpEmitter.h
Normal 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 */
|
||||
382
js/src/frontend/NameOpEmitter.cpp
Normal file
382
js/src/frontend/NameOpEmitter.cpp
Normal 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;
|
||||
}
|
||||
193
js/src/frontend/NameOpEmitter.h
Normal file
193
js/src/frontend/NameOpEmitter.h
Normal 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 */
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
275
js/src/frontend/PropOpEmitter.cpp
Normal file
275
js/src/frontend/PropOpEmitter.cpp
Normal 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;
|
||||
}
|
||||
269
js/src/frontend/PropOpEmitter.h
Normal file
269
js/src/frontend/PropOpEmitter.h
Normal 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 */
|
||||
28
js/src/frontend/ValueUsage.h
Normal file
28
js/src/frontend/ValueUsage.h
Normal 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 */
|
||||
|
|
@ -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',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue