mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-08 08:48:39 +09:00
Merge remote-tracking branch 'origin/tracking' into custom
This commit is contained in:
commit
6a7219fb70
21 changed files with 4130 additions and 1568 deletions
|
|
@ -279,6 +279,13 @@ ShadowRoot::InsertSheet(StyleSheet* aSheet,
|
|||
}
|
||||
|
||||
nsINode* sheetOwningNode = SheetAt(i)->GetOwnerNode();
|
||||
|
||||
if (!sheetOwningNode) {
|
||||
// Keep moving; all sheets with a sheetOwner come after all
|
||||
// sheets without an owning Node
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nsContentUtils::PositionIsBefore(aLinkingContent, sheetOwningNode)) {
|
||||
InsertSheetAt(i, *aSheet);
|
||||
mProtoBinding->InsertStyleSheetAt(i, aSheet);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -14,9 +14,11 @@
|
|||
#include "jsscript.h"
|
||||
|
||||
#include "ds/InlineTable.h"
|
||||
#include "frontend/JumpList.h"
|
||||
#include "frontend/Parser.h"
|
||||
#include "frontend/SharedContext.h"
|
||||
#include "frontend/SourceNotes.h"
|
||||
#include "frontend/ValueUsage.h"
|
||||
#include "vm/Interpreter.h"
|
||||
|
||||
class OptionalEmitter;
|
||||
|
|
@ -118,69 +120,13 @@ static size_t MaxSrcNotesLength = INT32_MAX;
|
|||
typedef Vector<jsbytecode, 256> BytecodeVector;
|
||||
typedef Vector<jssrcnote, 64> SrcNotesVector;
|
||||
|
||||
// Linked list of jump instructions that need to be patched. The linked list is
|
||||
// stored in the bytes of the incomplete bytecode that will be patched, so no
|
||||
// extra memory is needed, and patching the instructions destroys the list.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// JumpList brList;
|
||||
// if (!emitJump(JSOP_IFEQ, &brList))
|
||||
// return false;
|
||||
// ...
|
||||
// JumpTarget label;
|
||||
// if (!emitJumpTarget(&label))
|
||||
// return false;
|
||||
// ...
|
||||
// if (!emitJump(JSOP_GOTO, &brList))
|
||||
// return false;
|
||||
// ...
|
||||
// patchJumpsToTarget(brList, label);
|
||||
//
|
||||
// +-> -1
|
||||
// |
|
||||
// |
|
||||
// ifeq .. <+ + +-+ ifeq ..
|
||||
// .. | | ..
|
||||
// label: | +-> label:
|
||||
// jumptarget | | jumptarget
|
||||
// .. | | ..
|
||||
// goto .. <+ + +-+ goto .. <+
|
||||
// | |
|
||||
// | |
|
||||
// + +
|
||||
// brList brList
|
||||
//
|
||||
// | ^
|
||||
// +------- patchJumpsToTarget -------+
|
||||
//
|
||||
|
||||
// Offset of a jump target instruction, used for patching jump instructions.
|
||||
struct JumpTarget {
|
||||
ptrdiff_t offset;
|
||||
};
|
||||
|
||||
struct JumpList {
|
||||
// -1 is used to mark the end of jump lists.
|
||||
JumpList() : offset(-1) {}
|
||||
ptrdiff_t offset;
|
||||
|
||||
// Add a jump instruction to the list.
|
||||
void push(jsbytecode* code, ptrdiff_t jumpOffset);
|
||||
|
||||
// Patch all jump instructions in this list to jump to `target`. This
|
||||
// clobbers the list.
|
||||
void patchAll(jsbytecode* code, JumpTarget target);
|
||||
};
|
||||
|
||||
enum class ValueUsage {
|
||||
WantValue,
|
||||
IgnoreValue
|
||||
};
|
||||
class CallOrNewEmitter;
|
||||
class ElemOpEmitter;
|
||||
class PropOpEmitter;
|
||||
class TDZCheckCache;
|
||||
|
||||
struct MOZ_STACK_CLASS BytecodeEmitter
|
||||
{
|
||||
class TDZCheckCache;
|
||||
class NestableControl;
|
||||
class EmitterScope;
|
||||
|
||||
|
|
@ -500,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);
|
||||
|
||||
|
|
@ -520,6 +469,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter
|
|||
|
||||
MOZ_MUST_USE bool emitThisLiteral(ParseNode* pn);
|
||||
MOZ_MUST_USE bool emitGetFunctionThis(ParseNode* pn);
|
||||
MOZ_MUST_USE bool emitGetFunctionThis(const mozilla::Maybe<uint32_t>& offset);
|
||||
MOZ_MUST_USE bool emitGetThisForSuperBase(ParseNode* pn);
|
||||
MOZ_MUST_USE bool emitSetThis(ParseNode* pn);
|
||||
MOZ_MUST_USE bool emitCheckDerivedClassConstructorReturn();
|
||||
|
|
@ -533,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);
|
||||
|
||||
|
|
@ -546,7 +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(ParseNode* pn, 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);
|
||||
|
|
@ -578,44 +530,15 @@ struct MOZ_STACK_CLASS BytecodeEmitter
|
|||
MOZ_MUST_USE bool emitArgOp(JSOp op, uint16_t slot);
|
||||
MOZ_MUST_USE bool emitEnvCoordOp(JSOp op, EnvironmentCoordinate ec);
|
||||
|
||||
MOZ_MUST_USE bool emitGetNameAtLocation(JSAtom* name, const NameLocation& loc,
|
||||
bool callContext = false);
|
||||
MOZ_MUST_USE bool emitGetName(JSAtom* name, bool callContext = false) {
|
||||
return emitGetNameAtLocation(name, lookupName(name), callContext);
|
||||
}
|
||||
MOZ_MUST_USE bool emitGetName(ParseNode* pn, bool callContext = false);
|
||||
|
||||
template <typename RHSEmitter>
|
||||
MOZ_MUST_USE bool emitSetOrInitializeNameAtLocation(HandleAtom name, const NameLocation& loc,
|
||||
RHSEmitter emitRhs, bool initialize);
|
||||
template <typename RHSEmitter>
|
||||
MOZ_MUST_USE bool emitSetOrInitializeName(HandleAtom name, RHSEmitter emitRhs,
|
||||
bool initialize)
|
||||
{
|
||||
return emitSetOrInitializeNameAtLocation(name, lookupName(name), emitRhs, initialize);
|
||||
}
|
||||
template <typename RHSEmitter>
|
||||
MOZ_MUST_USE bool emitSetName(ParseNode* pn, RHSEmitter emitRhs) {
|
||||
RootedAtom name(cx, pn->name());
|
||||
return emitSetName(name, emitRhs);
|
||||
}
|
||||
template <typename RHSEmitter>
|
||||
MOZ_MUST_USE bool emitSetName(HandleAtom name, RHSEmitter emitRhs) {
|
||||
return emitSetOrInitializeName(name, emitRhs, false);
|
||||
}
|
||||
template <typename RHSEmitter>
|
||||
MOZ_MUST_USE bool emitInitializeName(ParseNode* pn, RHSEmitter emitRhs) {
|
||||
RootedAtom name(cx, pn->name());
|
||||
return emitInitializeName(name, emitRhs);
|
||||
}
|
||||
template <typename RHSEmitter>
|
||||
MOZ_MUST_USE bool emitInitializeName(HandleAtom name, RHSEmitter emitRhs) {
|
||||
return emitSetOrInitializeName(name, emitRhs, true);
|
||||
MOZ_MUST_USE bool emitGetNameAtLocation(JSAtom* name, const NameLocation& loc);
|
||||
MOZ_MUST_USE bool emitGetName(JSAtom* name) {
|
||||
return emitGetNameAtLocation(name, lookupName(name));
|
||||
}
|
||||
MOZ_MUST_USE bool emitGetName(ParseNode* pn);
|
||||
|
||||
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,
|
||||
|
|
@ -644,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);
|
||||
|
|
@ -659,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);
|
||||
|
|
@ -769,14 +692,14 @@ 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);
|
||||
|
||||
MOZ_MUST_USE bool emitCallSiteObject(ParseNode* pn);
|
||||
MOZ_MUST_USE bool emitTemplateString(ParseNode* pn);
|
||||
MOZ_MUST_USE bool emitAssignment(ParseNode* lhs, JSOp op, ParseNode* rhs);
|
||||
MOZ_MUST_USE bool emitAssignment(ParseNode* lhs, JSOp compoundOp, ParseNode* rhs);
|
||||
|
||||
MOZ_MUST_USE bool emitReturn(ParseNode* pn);
|
||||
MOZ_MUST_USE bool emitStatement(ParseNode* pn);
|
||||
|
|
@ -792,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);
|
||||
|
|
@ -828,24 +749,23 @@ struct MOZ_STACK_CLASS BytecodeEmitter
|
|||
MOZ_MUST_USE bool emitConditionalExpression(ConditionalExpression& conditional,
|
||||
ValueUsage valueUsage = ValueUsage::WantValue);
|
||||
|
||||
MOZ_MUST_USE bool isRestParameter(ParseNode* pn, bool* result);
|
||||
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* firstArgNode);
|
||||
MOZ_MUST_USE bool emitArguments(ParseNode* firstArgNode, uint32_t argc,
|
||||
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);
|
||||
MOZ_MUST_USE bool emitCallOrNewArgumentsAndEnd(ParseNode* callNode,
|
||||
ParseNode* calleeNode,
|
||||
bool isCall,
|
||||
ValueUsage valueUsage);
|
||||
|
||||
MOZ_MUST_USE bool emitSelfHostedCallFunction(ParseNode* pn);
|
||||
MOZ_MUST_USE bool emitSelfHostedResumeGenerator(ParseNode* pn);
|
||||
|
|
@ -887,8 +807,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 */
|
||||
231
js/src/frontend/IfEmitter.cpp
Normal file
231
js/src/frontend/IfEmitter.cpp
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
/* -*- 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/IfEmitter.h"
|
||||
|
||||
#include "frontend/BytecodeEmitter.h"
|
||||
#include "frontend/SourceNotes.h"
|
||||
#include "vm/Opcodes.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::frontend;
|
||||
|
||||
IfEmitter::IfEmitter(BytecodeEmitter* bce, Kind kind)
|
||||
: bce_(bce),
|
||||
noteIndex_(-1),
|
||||
thenDepth_(0),
|
||||
kind_(kind)
|
||||
#ifdef DEBUG
|
||||
, pushed_(0),
|
||||
calculatedPushed_(false),
|
||||
state_(State::Start)
|
||||
#endif
|
||||
{}
|
||||
|
||||
IfEmitter::IfEmitter(BytecodeEmitter* bce)
|
||||
: IfEmitter(bce, Kind::MayContainLexicalAccessInBranch)
|
||||
{}
|
||||
|
||||
bool
|
||||
IfEmitter::emitIfInternal(SrcNoteType type)
|
||||
{
|
||||
MOZ_ASSERT_IF(state_ == State::ElseIf, tdzCache_.isSome());
|
||||
MOZ_ASSERT_IF(state_ != State::ElseIf, tdzCache_.isNothing());
|
||||
|
||||
// The end of TDZCheckCache for cond for else-if.
|
||||
if (kind_ == Kind::MayContainLexicalAccessInBranch)
|
||||
tdzCache_.reset();
|
||||
|
||||
// Emit an annotated branch-if-false around the then part.
|
||||
if (!bce_->newSrcNote(type, ¬eIndex_))
|
||||
return false;
|
||||
if (!bce_->emitJump(JSOP_IFEQ, &jumpAroundThen_))
|
||||
return false;
|
||||
|
||||
// To restore stack depth in else part, save depth of the then part.
|
||||
#ifdef DEBUG
|
||||
// If DEBUG, this is also necessary to calculate |pushed_|.
|
||||
thenDepth_ = bce_->stackDepth;
|
||||
#else
|
||||
if (type == SRC_COND || type == SRC_IF_ELSE)
|
||||
thenDepth_ = bce_->stackDepth;
|
||||
#endif
|
||||
|
||||
// Enclose then-branch with TDZCheckCache.
|
||||
if (kind_ == Kind::MayContainLexicalAccessInBranch)
|
||||
tdzCache_.emplace(bce_);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
IfEmitter::calculateOrCheckPushed()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (!calculatedPushed_) {
|
||||
pushed_ = bce_->stackDepth - thenDepth_;
|
||||
calculatedPushed_ = true;
|
||||
} else {
|
||||
MOZ_ASSERT(pushed_ == bce_->stackDepth - thenDepth_);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
IfEmitter::emitThen()
|
||||
{
|
||||
MOZ_ASSERT(state_ == State::Start || state_ == State::ElseIf);
|
||||
if (!emitIfInternal(SRC_IF))
|
||||
return false;
|
||||
|
||||
#ifdef DEBUG
|
||||
state_ = State::Then;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
IfEmitter::emitCond()
|
||||
{
|
||||
MOZ_ASSERT(state_ == State::Start);
|
||||
if (!emitIfInternal(SRC_COND))
|
||||
return false;
|
||||
|
||||
#ifdef DEBUG
|
||||
state_ = State::Cond;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
IfEmitter::emitThenElse()
|
||||
{
|
||||
MOZ_ASSERT(state_ == State::Start || state_ == State::ElseIf);
|
||||
if (!emitIfInternal(SRC_IF_ELSE))
|
||||
return false;
|
||||
|
||||
#ifdef DEBUG
|
||||
state_ = State::ThenElse;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
IfEmitter::emitElseInternal()
|
||||
{
|
||||
calculateOrCheckPushed();
|
||||
|
||||
// The end of TDZCheckCache for then-clause.
|
||||
if (kind_ == Kind::MayContainLexicalAccessInBranch) {
|
||||
MOZ_ASSERT(tdzCache_.isSome());
|
||||
tdzCache_.reset();
|
||||
}
|
||||
|
||||
// Emit a jump from the end of our then part around the else part. The
|
||||
// patchJumpsToTarget call at the bottom of this function will fix up
|
||||
// the offset with jumpsAroundElse value.
|
||||
if (!bce_->emitJump(JSOP_GOTO, &jumpsAroundElse_))
|
||||
return false;
|
||||
|
||||
// Ensure the branch-if-false comes here, then emit the else.
|
||||
if (!bce_->emitJumpTargetAndPatch(jumpAroundThen_))
|
||||
return false;
|
||||
|
||||
// Annotate SRC_IF_ELSE or SRC_COND with the offset from branch to
|
||||
// jump, for IonMonkey's benefit. We can't just "back up" from the pc
|
||||
// of the else clause, because we don't know whether an extended
|
||||
// jump was required to leap from the end of the then clause over
|
||||
// the else clause.
|
||||
if (!bce_->setSrcNoteOffset(noteIndex_, 0,
|
||||
jumpsAroundElse_.offset - jumpAroundThen_.offset))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clear jumpAroundThen_ offset, to tell emitEnd there was an else part.
|
||||
jumpAroundThen_ = JumpList();
|
||||
|
||||
// Restore stack depth of the then part.
|
||||
bce_->stackDepth = thenDepth_;
|
||||
#ifdef DEBUG
|
||||
state_ = State::Else;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
IfEmitter::emitElse()
|
||||
{
|
||||
MOZ_ASSERT(state_ == State::ThenElse || state_ == State::Cond);
|
||||
|
||||
if (!emitElseInternal())
|
||||
return false;
|
||||
|
||||
// Enclose else-branch with TDZCheckCache.
|
||||
if (kind_ == Kind::MayContainLexicalAccessInBranch)
|
||||
tdzCache_.emplace(bce_);
|
||||
|
||||
#ifdef DEBUG
|
||||
state_ = State::Else;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
IfEmitter::emitElseIf()
|
||||
{
|
||||
MOZ_ASSERT(state_ == State::ThenElse);
|
||||
|
||||
if (!emitElseInternal())
|
||||
return false;
|
||||
|
||||
// Enclose cond for else-if with TDZCheckCache.
|
||||
if (kind_ == Kind::MayContainLexicalAccessInBranch)
|
||||
tdzCache_.emplace(bce_);
|
||||
|
||||
#ifdef DEBUG
|
||||
state_ = State::ElseIf;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
IfEmitter::emitEnd()
|
||||
{
|
||||
MOZ_ASSERT(state_ == State::Then || state_ == State::Else);
|
||||
// If there was an else part for the last branch, jumpAroundThen_ is
|
||||
// already fixed up when emitting the else part.
|
||||
MOZ_ASSERT_IF(state_ == State::Then, jumpAroundThen_.offset != -1);
|
||||
MOZ_ASSERT_IF(state_ == State::Else, jumpAroundThen_.offset == -1);
|
||||
|
||||
// The end of TDZCheckCache for then or else-clause.
|
||||
if (kind_ == Kind::MayContainLexicalAccessInBranch) {
|
||||
MOZ_ASSERT(tdzCache_.isSome());
|
||||
tdzCache_.reset();
|
||||
}
|
||||
|
||||
calculateOrCheckPushed();
|
||||
|
||||
if (jumpAroundThen_.offset != -1) {
|
||||
// No else part for the last branch, fixup the branch-if-false to
|
||||
// come here.
|
||||
if (!bce_->emitJumpTargetAndPatch(jumpAroundThen_))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Patch all the jumps around else parts.
|
||||
if (!bce_->emitJumpTargetAndPatch(jumpsAroundElse_))
|
||||
return false;
|
||||
|
||||
#ifdef DEBUG
|
||||
state_ = State::End;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
InternalIfEmitter::InternalIfEmitter(BytecodeEmitter* bce)
|
||||
: IfEmitter(bce, Kind::NoLexicalAccessInBranch)
|
||||
{}
|
||||
220
js/src/frontend/IfEmitter.h
Normal file
220
js/src/frontend/IfEmitter.h
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
/* -*- 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_IfEmitter_h
|
||||
#define frontend_IfEmitter_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "frontend/JumpList.h"
|
||||
#include "frontend/SourceNotes.h"
|
||||
#include "frontend/TDZCheckCache.h"
|
||||
|
||||
namespace js {
|
||||
namespace frontend {
|
||||
|
||||
struct BytecodeEmitter;
|
||||
|
||||
// Class for emitting bytecode for blocks like if-then-else.
|
||||
//
|
||||
// This class can be used to emit single if-then-else block, or cascading
|
||||
// else-if blocks.
|
||||
//
|
||||
// Usage: (check for the return value is omitted for simplicity)
|
||||
//
|
||||
// `if (cond) then_block`
|
||||
// IfEmitter ifThen(this);
|
||||
// emit(cond);
|
||||
// ifThen.emitThen();
|
||||
// emit(then_block);
|
||||
// ifThen.emitEnd();
|
||||
//
|
||||
// `if (cond) then_block else else_block`
|
||||
// IfEmitter ifThenElse(this);
|
||||
// emit(cond);
|
||||
// ifThenElse.emitThenElse();
|
||||
// emit(then_block);
|
||||
// ifThenElse.emitElse();
|
||||
// emit(else_block);
|
||||
// ifThenElse.emitEnd();
|
||||
//
|
||||
// `if (c1) b1 else if (c2) b2 else if (c3) b3 else b4`
|
||||
// IfEmitter ifThenElse(this);
|
||||
// emit(c1);
|
||||
// ifThenElse.emitThenElse();
|
||||
// emit(b1);
|
||||
// ifThenElse.emitElseIf();
|
||||
// emit(c2);
|
||||
// ifThenElse.emitThenElse();
|
||||
// emit(b2);
|
||||
// ifThenElse.emitElseIf();
|
||||
// emit(c3);
|
||||
// ifThenElse.emitThenElse();
|
||||
// emit(b3);
|
||||
// ifThenElse.emitElse();
|
||||
// emit(b4);
|
||||
// ifThenElse.emitEnd();
|
||||
//
|
||||
// `cond ? then_expr : else_expr`
|
||||
// IfEmitter condElse(this);
|
||||
// emit(cond);
|
||||
// condElse.emitCond();
|
||||
// emit(then_block);
|
||||
// condElse.emitElse();
|
||||
// emit(else_block);
|
||||
// condElse.emitEnd();
|
||||
//
|
||||
class MOZ_STACK_CLASS IfEmitter
|
||||
{
|
||||
public:
|
||||
// Whether the then-clause, the else-clause, or else-if condition may
|
||||
// contain declaration or access to lexical variables, which means they
|
||||
// should have their own TDZCheckCache. Basically TDZCheckCache should be
|
||||
// created for each basic block, which then-clause, else-clause, and
|
||||
// else-if condition are, but for internally used branches which are
|
||||
// known not to touch lexical variables we can skip creating TDZCheckCache
|
||||
// for them.
|
||||
//
|
||||
// See the comment for TDZCheckCache class for more details.
|
||||
enum class Kind {
|
||||
// For syntactic branches (if, if-else, and conditional expression),
|
||||
// which basically may contain declaration or accesses to lexical
|
||||
// variables inside then-clause, else-clause, and else-if condition.
|
||||
MayContainLexicalAccessInBranch,
|
||||
|
||||
// For internally used branches which don't touch lexical variables
|
||||
// inside then-clause, else-clause, nor else-if condition.
|
||||
NoLexicalAccessInBranch
|
||||
};
|
||||
|
||||
private:
|
||||
BytecodeEmitter* bce_;
|
||||
|
||||
// Jump around the then clause, to the beginning of the else clause.
|
||||
JumpList jumpAroundThen_;
|
||||
|
||||
// Jump around the else clause, to the end of the entire branch.
|
||||
JumpList jumpsAroundElse_;
|
||||
|
||||
// Annotation index for IonBuilder
|
||||
unsigned noteIndex_;
|
||||
|
||||
// The stack depth before emitting the then block.
|
||||
// Used for restoring stack depth before emitting the else block.
|
||||
// Also used for assertion to make sure then and else blocks pushed the
|
||||
// same number of values.
|
||||
int32_t thenDepth_;
|
||||
|
||||
Kind kind_;
|
||||
mozilla::Maybe<TDZCheckCache> tdzCache_;
|
||||
|
||||
#ifdef DEBUG
|
||||
// The number of values pushed in the then and else blocks.
|
||||
int32_t pushed_;
|
||||
bool calculatedPushed_;
|
||||
|
||||
// The state of this emitter.
|
||||
//
|
||||
// +-------+ emitCond +------+ emitElse +------+ emitEnd +-----+
|
||||
// | Start |-+--------->| Cond |--------->| Else |------>+------->| End |
|
||||
// +-------+ | +------+ +------+ ^ +-----+
|
||||
// | |
|
||||
// v emitThen +------+ |
|
||||
// +->+--------->| Then |------------------------>+
|
||||
// ^ | +------+ ^
|
||||
// | | |
|
||||
// | | +---+
|
||||
// | | |
|
||||
// | | emitThenElse +----------+ emitElse +------+ |
|
||||
// | +------------->| ThenElse |-+--------->| Else |-+
|
||||
// | +----------+ | +------+
|
||||
// | |
|
||||
// | | emitElseIf +--------+
|
||||
// | +----------->| ElseIf |-+
|
||||
// | +--------+ |
|
||||
// | |
|
||||
// +------------------------------------------------------+
|
||||
enum class State {
|
||||
// The initial state.
|
||||
Start,
|
||||
|
||||
// After calling emitThen.
|
||||
Then,
|
||||
|
||||
// After calling emitCond.
|
||||
Cond,
|
||||
|
||||
// After calling emitThenElse.
|
||||
ThenElse,
|
||||
|
||||
// After calling emitElse.
|
||||
Else,
|
||||
|
||||
// After calling emitElseIf.
|
||||
ElseIf,
|
||||
|
||||
// After calling emitEnd.
|
||||
End
|
||||
};
|
||||
State state_;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
// For InternalIfEmitter.
|
||||
IfEmitter(BytecodeEmitter* bce, Kind kind);
|
||||
|
||||
public:
|
||||
explicit IfEmitter(BytecodeEmitter* bce);
|
||||
|
||||
MOZ_MUST_USE bool emitThen();
|
||||
MOZ_MUST_USE bool emitCond();
|
||||
MOZ_MUST_USE bool emitThenElse();
|
||||
|
||||
MOZ_MUST_USE bool emitElse();
|
||||
MOZ_MUST_USE bool emitElseIf();
|
||||
|
||||
MOZ_MUST_USE bool emitEnd();
|
||||
|
||||
#ifdef DEBUG
|
||||
// Returns the number of values pushed onto the value stack inside
|
||||
// `then_block` and `else_block`.
|
||||
// Can be used in assertion after emitting if-then-else.
|
||||
int32_t pushed() const {
|
||||
return pushed_;
|
||||
}
|
||||
|
||||
// Returns the number of values popped onto the value stack inside
|
||||
// `then_block` and `else_block`.
|
||||
// Can be used in assertion after emitting if-then-else.
|
||||
int32_t popped() const {
|
||||
return -pushed_;
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
MOZ_MUST_USE bool emitIfInternal(SrcNoteType type);
|
||||
void calculateOrCheckPushed();
|
||||
MOZ_MUST_USE bool emitElseInternal();
|
||||
};
|
||||
|
||||
// Class for emitting bytecode for blocks like if-then-else which doesn't touch
|
||||
// lexical variables.
|
||||
//
|
||||
// See the comments above NoLexicalAccessInBranch for more details when to use
|
||||
// this instead of IfEmitter.
|
||||
class MOZ_STACK_CLASS InternalIfEmitter : public IfEmitter
|
||||
{
|
||||
public:
|
||||
explicit InternalIfEmitter(BytecodeEmitter* bce);
|
||||
};
|
||||
|
||||
} /* namespace frontend */
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* frontend_IfEmitter_h */
|
||||
33
js/src/frontend/JumpList.cpp
Normal file
33
js/src/frontend/JumpList.cpp
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/* -*- 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/JumpList.h"
|
||||
|
||||
#include "jsopcode.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::frontend;
|
||||
|
||||
void
|
||||
JumpList::push(jsbytecode* code, ptrdiff_t jumpOffset)
|
||||
{
|
||||
SET_JUMP_OFFSET(&code[jumpOffset], offset - jumpOffset);
|
||||
offset = jumpOffset;
|
||||
}
|
||||
|
||||
void
|
||||
JumpList::patchAll(jsbytecode* code, JumpTarget target)
|
||||
{
|
||||
ptrdiff_t delta;
|
||||
for (ptrdiff_t jumpOffset = offset; jumpOffset != -1; jumpOffset += delta) {
|
||||
jsbytecode* pc = &code[jumpOffset];
|
||||
MOZ_ASSERT(IsJumpOpcode(JSOp(*pc)) || JSOp(*pc) == JSOP_LABEL);
|
||||
delta = GET_JUMP_OFFSET(pc);
|
||||
MOZ_ASSERT(delta < 0);
|
||||
ptrdiff_t span = target.offset - jumpOffset;
|
||||
SET_JUMP_OFFSET(pc, span);
|
||||
}
|
||||
}
|
||||
76
js/src/frontend/JumpList.h
Normal file
76
js/src/frontend/JumpList.h
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
/* -*- 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_JumpList_h
|
||||
#define frontend_JumpList_h
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "js/TypeDecls.h"
|
||||
#include "jsbytecode.h"
|
||||
|
||||
namespace js {
|
||||
namespace frontend {
|
||||
|
||||
// Linked list of jump instructions that need to be patched. The linked list is
|
||||
// stored in the bytes of the incomplete bytecode that will be patched, so no
|
||||
// extra memory is needed, and patching the instructions destroys the list.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// JumpList brList;
|
||||
// if (!emitJump(JSOP_IFEQ, &brList))
|
||||
// return false;
|
||||
// ...
|
||||
// JumpTarget label;
|
||||
// if (!emitJumpTarget(&label))
|
||||
// return false;
|
||||
// ...
|
||||
// if (!emitJump(JSOP_GOTO, &brList))
|
||||
// return false;
|
||||
// ...
|
||||
// patchJumpsToTarget(brList, label);
|
||||
//
|
||||
// +-> -1
|
||||
// |
|
||||
// |
|
||||
// ifeq .. <+ + +-+ ifeq ..
|
||||
// .. | | ..
|
||||
// label: | +-> label:
|
||||
// jumptarget | | jumptarget
|
||||
// .. | | ..
|
||||
// goto .. <+ + +-+ goto .. <+
|
||||
// | |
|
||||
// | |
|
||||
// + +
|
||||
// brList brList
|
||||
//
|
||||
// | ^
|
||||
// +------- patchJumpsToTarget -------+
|
||||
//
|
||||
|
||||
// Offset of a jump target instruction, used for patching jump instructions.
|
||||
struct JumpTarget {
|
||||
ptrdiff_t offset;
|
||||
};
|
||||
|
||||
struct JumpList {
|
||||
JumpList() {}
|
||||
// -1 is used to mark the end of jump lists.
|
||||
ptrdiff_t offset = -1;
|
||||
|
||||
// Add a jump instruction to the list.
|
||||
void push(jsbytecode* code, ptrdiff_t jumpOffset);
|
||||
|
||||
// Patch all jump instructions in this list to jump to `target`. This
|
||||
// clobbers the list.
|
||||
void patchAll(jsbytecode* code, JumpTarget target);
|
||||
};
|
||||
|
||||
} /* namespace frontend */
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* frontend_JumpList_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 */
|
||||
|
|
@ -886,6 +886,10 @@ struct UnaryNode : public ParseNode
|
|||
pn_kid = kid;
|
||||
}
|
||||
|
||||
static bool test(const ParseNode& node) {
|
||||
return node.isArity(PN_UNARY);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void dump(int indent);
|
||||
#endif
|
||||
|
|
@ -996,6 +1000,10 @@ struct NameNode : public ParseNode
|
|||
pn_expr = nullptr;
|
||||
}
|
||||
|
||||
static bool test(const ParseNode& node) {
|
||||
return node.isArity(PN_NAME);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void dump(int indent);
|
||||
#endif
|
||||
|
|
@ -1248,6 +1256,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 +1314,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 */
|
||||
76
js/src/frontend/TDZCheckCache.cpp
Normal file
76
js/src/frontend/TDZCheckCache.cpp
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
/* -*- 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/TDZCheckCache.h"
|
||||
|
||||
#include "frontend/BytecodeEmitter.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::frontend;
|
||||
|
||||
using mozilla::Maybe;
|
||||
using mozilla::Nothing;
|
||||
using mozilla::Some;
|
||||
|
||||
TDZCheckCache::TDZCheckCache(BytecodeEmitter* bce)
|
||||
: Nestable<TDZCheckCache>(&bce->innermostTDZCheckCache),
|
||||
cache_(bce->cx->frontendCollectionPool())
|
||||
{}
|
||||
|
||||
bool
|
||||
TDZCheckCache::ensureCache(BytecodeEmitter* bce)
|
||||
{
|
||||
return cache_ || cache_.acquire(bce->cx);
|
||||
}
|
||||
|
||||
Maybe<MaybeCheckTDZ>
|
||||
TDZCheckCache::needsTDZCheck(BytecodeEmitter* bce, JSAtom* name)
|
||||
{
|
||||
if (!ensureCache(bce))
|
||||
return Nothing();
|
||||
|
||||
CheckTDZMap::AddPtr p = cache_->lookupForAdd(name);
|
||||
if (p)
|
||||
return Some(p->value().wrapped);
|
||||
|
||||
MaybeCheckTDZ rv = CheckTDZ;
|
||||
for (TDZCheckCache* it = enclosing(); it; it = it->enclosing()) {
|
||||
if (it->cache_) {
|
||||
if (CheckTDZMap::Ptr p2 = it->cache_->lookup(name)) {
|
||||
rv = p2->value();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!cache_->add(p, name, rv)) {
|
||||
ReportOutOfMemory(bce->cx);
|
||||
return Nothing();
|
||||
}
|
||||
|
||||
return Some(rv);
|
||||
}
|
||||
|
||||
bool
|
||||
TDZCheckCache::noteTDZCheck(BytecodeEmitter* bce, JSAtom* name,
|
||||
MaybeCheckTDZ check)
|
||||
{
|
||||
if (!ensureCache(bce))
|
||||
return false;
|
||||
|
||||
CheckTDZMap::AddPtr p = cache_->lookupForAdd(name);
|
||||
if (p) {
|
||||
MOZ_ASSERT(!check, "TDZ only needs to be checked once per binding per basic block.");
|
||||
p->value() = check;
|
||||
} else {
|
||||
if (!cache_->add(p, name, check)) {
|
||||
ReportOutOfMemory(bce->cx);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
55
js/src/frontend/TDZCheckCache.h
Normal file
55
js/src/frontend/TDZCheckCache.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/* -*- 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_TDZCheckCache_h
|
||||
#define frontend_TDZCheckCache_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
|
||||
#include "frontend/SharedContext.h" // for Nestable
|
||||
#include "frontend/NameCollections.h"
|
||||
#include "js/TypeDecls.h"
|
||||
#include "vm/Stack.h"
|
||||
|
||||
namespace js {
|
||||
namespace frontend {
|
||||
|
||||
struct BytecodeEmitter;
|
||||
|
||||
// A cache that tracks Temporal Dead Zone (TDZ) checks, so that any use of a
|
||||
// lexical variable that's dominated by an earlier use, or by evaluation of its
|
||||
// declaration (which will initialize it, perhaps to |undefined|), doesn't have
|
||||
// to redundantly check that the lexical variable has been initialized
|
||||
//
|
||||
// Each basic block should have a TDZCheckCache in scope. Some NestableControl
|
||||
// subclasses contain a TDZCheckCache.
|
||||
//
|
||||
// When a scope containing lexical variables is entered, all such variables are
|
||||
// marked as CheckTDZ. When a lexical variable is accessed, its entry is
|
||||
// checked. If it's CheckTDZ, a JSOP_CHECKLEXICAL is emitted and then the
|
||||
// entry is marked DontCheckTDZ. If it's DontCheckTDZ, no check is emitted
|
||||
// because a prior check would have already failed. Finally, because
|
||||
// evaluating a lexical variable declaration initializes it (after any
|
||||
// initializer is evaluated), evaluating a lexical declaration marks its entry
|
||||
// as DontCheckTDZ.
|
||||
class TDZCheckCache : public Nestable<TDZCheckCache>
|
||||
{
|
||||
PooledMapPtr<CheckTDZMap> cache_;
|
||||
|
||||
MOZ_MUST_USE bool ensureCache(BytecodeEmitter* bce);
|
||||
|
||||
public:
|
||||
explicit TDZCheckCache(BytecodeEmitter* bce);
|
||||
|
||||
mozilla::Maybe<MaybeCheckTDZ> needsTDZCheck(BytecodeEmitter* bce, JSAtom* name);
|
||||
MOZ_MUST_USE bool noteTDZCheck(BytecodeEmitter* bce, JSAtom* name, MaybeCheckTDZ check);
|
||||
};
|
||||
|
||||
} /* namespace frontend */
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* frontend_TDZCheckCache_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,9 +140,16 @@ 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',
|
||||
'gc/Barrier.cpp',
|
||||
|
|
|
|||
|
|
@ -1965,17 +1965,35 @@ static bool SelectorMatches(Element* aElement,
|
|||
|
||||
case CSSPseudoClassType::host:
|
||||
{
|
||||
ShadowRoot* shadow = aElement->GetShadowRoot();
|
||||
// In order to match :host, the element must be a shadow root host,
|
||||
// we must be matching only against host pseudo selectors, and the
|
||||
// selector's context must be the shadow root (the selector must be
|
||||
// featureless, the left-most selector, and be in a shadow root
|
||||
// style).
|
||||
if (!aElement->GetShadowRoot() ||
|
||||
if (!shadow ||
|
||||
aSelector->HasFeatureSelectors() ||
|
||||
aSelectorFlags & SelectorMatchesFlags::IS_HOST_INACCESSIBLE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We're matching :host from inside the shadow root.
|
||||
if (!aTreeMatchContext.mOnlyMatchHostPseudo) {
|
||||
// Check if the element has the same shadow root.
|
||||
if (aTreeMatchContext.mScopedRoot) {
|
||||
if (shadow !=
|
||||
aTreeMatchContext.mScopedRoot->GetShadowRoot()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// We were called elsewhere.
|
||||
}
|
||||
|
||||
// Reject if the next selector is an explicit universal selector.
|
||||
if (aSelector->mNext && aSelector->mNext->mExplicitUniversal) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The :host selector may also be be functional, with a compound
|
||||
// selector. If this is the case, then also ensure that the host
|
||||
// element matches against the compound selector.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue