/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef frontend_CallOrNewEmitter_h #define frontend_CallOrNewEmitter_h #include "mozilla/Attributes.h" #include "mozilla/Maybe.h" #include #include "frontend/ElemOpEmitter.h" #include "frontend/IfEmitter.h" #include "frontend/PrivateOpEmitter.h" #include "frontend/PropOpEmitter.h" #include "frontend/ValueUsage.h" #include "vm/BytecodeUtil.h" #include "vm/Opcodes.h" namespace js { namespace frontend { struct BytecodeEmitter; class TaggedParserAtomIndex; // 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(print); // cone.emitThis(); // cone.prepareForNonSpreadArguments(); // emit(arg); // cone.emitEnd(1, 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, 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, offset_of_callee); // // `callee.#method(arg);` // CallOrNewEmitter cone(this, JSOp::Call, // CallOrNewEmitter::ArgumentsKind::Other, // ValueUsage::WantValue); // PrivateOpEmitter& xoe = cone.prepareForPrivateCallee(); // ... emit `callee.#method` with `xoe` here... // cone.prepareForNonSpreadArguments(); // emit(arg); // cone.emitEnd(1, 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, 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, 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, offset_of_callee); // // `print(...arg);` // CallOrNewEmitter cone(this, JSOp::SpreadCall, // CallOrNewEmitter::ArgumentsKind::SingleSpread, // ValueUsage::WantValue); // cone.emitNameCallee(print); // cone.emitThis(); // if (cone.wantSpreadOperand()) { // emit(arg) // } // cone.emitSpreadArgumentsTest(); // emit([...arg]); // cone.emitEnd(1, offset_of_callee); // // `new f(arg);` // CallOrNewEmitter cone(this, JSOp::New, // CallOrNewEmitter::ArgumentsKind::Other, // ValueUsage::WantValue); // cone.emitNameCallee(f); // cone.emitThis(); // cone.prepareForNonSpreadArguments(); // emit(arg); // cone.emitEnd(1, offset_of_callee); // class MOZ_STACK_CLASS CallOrNewEmitter { public: enum class ArgumentsKind { Other, // Specify this for the following case: // // g(...input); // // This enables optimization to avoid allocating an intermediate array // for spread operation. // // wantSpreadOperand() returns true when this is specified. SingleSpread, // Used for default derived class constructors: // // constructor(...args) { // super(...args); // } // // The rest-parameter is directly passed through to the `super` call without // using the iteration protocol. PassthroughRest, }; private: BytecodeEmitter* bce_; // The opcode for the call or new. JSOp op_; // Whether the call is a spread call with single parameter or not. // See the comment in emitSpreadArgumentsTest for more details. ArgumentsKind argumentsKind_; // The branch for spread call optimization. mozilla::Maybe ifNotOptimizable_; mozilla::Maybe poe_; mozilla::Maybe eoe_; mozilla::Maybe xoe_; // The state of this emitter. // // +-------+ emitNameCallee +------------+ // | Start |-+------------------------->| NameCallee |------+ // +-------+ | +------------+ | // | | // | prepareForPropCallee +------------+ v // +------------------------->| PropCallee |----->+ // | +------------+ | // | | // | prepareForElemCallee +------------+ v // +------------------------->| ElemCallee |----->+ // | +------------+ | // | | // | prepareForPrivateCallee +---------------+ v // +------------------------->| PrivateCallee |-->+ // | +---------------+ | // | | // | prepareForFunctionCallee +----------------+ v // +------------------------->| FunctionCallee |->+ // | +----------------+ | // | | // | emitSuperCallee +-------------+ v // +------------------------->| SuperCallee |---->+ // | +-------------+ | // | | // | prepareForOtherCallee +-------------+ v // +------------------------->| OtherCallee |---->+ // +-------------+ | // | // +--------------------------------------------------------+ // | // | emitThis +------+ // +--------->| This |-+ // +------+ | // | // +-------------------+ // | // | [!isSpread] // | prepareForNonSpreadArguments +-----------+ emitEnd +-----+ // +------------------------------->+->| Arguments |-------->| End | // | ^ +-----------+ +-----+ // | | // | +<------------------------------------+ // | | | // | | emitSpreadArgumentsTestEnd | // | | | // | | +-----------------+ | // | +---------| SpreadIteration |------+ | // | +-----------------+ | | // | +----------------------------------+ | // | | | // | | wantSpreadIteration | // | | | // | | +---------------------+ | // | +---------| SpreadArgumentsTest |--+ | // | +---------------------+ | | // | [isSpread] | | // | wantSpreadOperand +-------------------+ emitSpreadArgumentsTest | | // +-------------------->| WantSpreadOperand |-------------------------+ | // | +-------------------+ | // | | // | | // | | // | [isSpread] | // | prepareForSpreadArguments | // +----------------------------------------------------------------------+ enum class State { // The initial state. Start, // After calling emitNameCallee. NameCallee, // After calling prepareForPropCallee. PropCallee, // After calling prepareForElemCallee. ElemCallee, // After calling prepareForPrivateCallee. PrivateCallee, // After calling prepareForFunctionCallee. FunctionCallee, // After calling emitSuperCallee. SuperCallee, // After calling prepareForOtherCallee. OtherCallee, // After calling emitThis. This, // After calling wantSpreadOperand. WantSpreadOperand, // After calling emitSpreadArgumentsTest. SpreadArgumentsTest, // After calling wantSpreadIteration. SpreadIteration, // After calling prepareForNonSpreadArguments. Arguments, // After calling emitEnd. End }; State state_ = State::Start; public: CallOrNewEmitter(BytecodeEmitter* bce, JSOp op, ArgumentsKind argumentsKind, ValueUsage valueUsage); private: [[nodiscard]] bool isCall() const { return op_ == JSOp::Call || op_ == JSOp::CallIgnoresRv || op_ == JSOp::SpreadCall || isEval(); } [[nodiscard]] bool isNew() const { return op_ == JSOp::New || op_ == JSOp::SpreadNew; } [[nodiscard]] bool isSuperCall() const { return op_ == JSOp::SuperCall || op_ == JSOp::SpreadSuperCall; } [[nodiscard]] bool isEval() const { return op_ == JSOp::Eval || op_ == JSOp::StrictEval || op_ == JSOp::SpreadEval || op_ == JSOp::StrictSpreadEval; } [[nodiscard]] bool isSpread() const { return IsSpreadOp(op_); } [[nodiscard]] bool isSingleSpread() const { return argumentsKind_ == ArgumentsKind::SingleSpread; } [[nodiscard]] bool isPassthroughRest() const { return argumentsKind_ == ArgumentsKind::PassthroughRest; } public: [[nodiscard]] bool emitNameCallee(TaggedParserAtomIndex name); [[nodiscard]] PropOpEmitter& prepareForPropCallee(bool isSuperProp); [[nodiscard]] ElemOpEmitter& prepareForElemCallee(bool isSuperElem); [[nodiscard]] PrivateOpEmitter& prepareForPrivateCallee( TaggedParserAtomIndex privateName); [[nodiscard]] bool prepareForFunctionCallee(); [[nodiscard]] bool emitSuperCallee(); [[nodiscard]] bool prepareForOtherCallee(); [[nodiscard]] bool emitThis(); [[nodiscard]] bool prepareForNonSpreadArguments(); [[nodiscard]] bool prepareForSpreadArguments(); // See the usage in the comment at the top of the class. [[nodiscard]] bool wantSpreadOperand(); [[nodiscard]] bool emitSpreadArgumentsTest(); [[nodiscard]] bool emitSpreadArgumentsTestEnd(); [[nodiscard]] bool wantSpreadIteration(); // Parameters are the offset in the source code for each character below: // // callee(arg); // ^ // | // beginPos [[nodiscard]] bool emitEnd(uint32_t argc, uint32_t beginPos); }; } /* namespace frontend */ } /* namespace js */ #endif /* frontend_CallOrNewEmitter_h */